У попередній лекції ми познайомилися з базовими концепціями AOP. Але між "знати терміни" і "розуміти, як це працює" — велика різниця. Давайте зазирнемо під капот і розберемося в деталях.
1. Аспект: не просто модуль
Ми вже знаємо, що аспект — це модуль для крос-секційних задач. Але як Spring його створює і керує ним?
Життєвий цикл аспекту
Аспект в Spring - це не просто клас з анотацією @Aspect. Це повноцінний резидент Spring-контейнера зі своїм життєвим циклом:
- Ініціалізація: Spring створює аспекти на самому ранньому етапі завантаження контексту
- Проксування: на основі аспектів створюються проксі для цільових бінів
- Впровадження: аспект може використовувати інші біни через
@Autowired - Виконання: Spring викликає методи аспекту при спрацьовуванні тригерів
Особливості роботи аспектів
На відміну від звичайних бінів, аспекти мають кілька важливих особливостей:
- Пріоритизація: коли кілька аспектів націлені на один метод, порядок їх виконання критичний:
@Aspect @Order(1) // Спочатку перевіримо права public class SecurityAspect { ... } @Aspect @Order(2) // Потім запишемо в лог public class LoggingAspect { ... } - Самозастосування: за замовчуванням аспекти не застосовуються до методів всередині самих аспектів — це захист від рекурсії
2. Поінткат: тонке налаштування точок впровадження
У попередній лекції ми бачили базовий синтаксис поінткатів. Тепер розберемося, як Spring їх обробляє і які є просунуті можливості.
Анатомія поінтката
Поінткат — це не просто рядок з wildcard-ами. Це потужна мова опису точок впровадження:
@Pointcut("execution(* com.example.service.*.*(..)) && @annotation(Secured)")
public void securedMethods() {}
Тут відбувається наступне:
execution()— визначає патерн для зіставлення методів.&&— комбінує умови.@annotation()— перевіряє наявність анотації.
Композиція поінткатів
Поінткати можна комбінувати, створюючи більш складну логіку:
@Pointcut("inService() && secured()")
public void securedServiceMethods() {}
@Pointcut("within(com.example.service.*)")
public void inService() {}
@Pointcut("@annotation(Secured)")
public void secured() {}
Часто використовують такі шаблони:
execution(* com.example..*(..)): Всі методи в усьому пакетіcom.example.within(com.example.service.*): Методи в конкретних класах.@annotation(org.springframework.transaction.annotation.Transactional): Методи з певною анотацією.
Як поінткати працюють за кулісами?
Spring «прослуховує» визначення поінткатів і зв'язує їх з точками з'єднання (наприклад, методами), які відповідають шаблону. Ці точки з'єднання стануть місцем, де аспекти виконуватимуться.
3. Адвайс: механіка виконання
Ми знаємо про типи адвайсів (@Before, @After і т.д.), але як Spring їх виконує?
Порядок виконання адвайсів
Коли на метод накладено кілька адвайсів, вони виконуються в такому порядку:
@Around (початок)
@Before
→ Метод
@AfterReturning/@AfterThrowing
@After
@Around (кінець)
Доступ до контексту
В адвайсах доступна багата контекстна інформація:
@Before("execution(* *(..))")
public void beforeMethod(JoinPoint jp) {
String methodName = jp.getSignature().getName();
Object[] args = jp.getArgs();
Object target = jp.getTarget();
}
Приклад: Before
@Before("execution(* com.example.service.OrderService.placeOrder(..))")
public void logBeforePlaceOrder() {
System.out.println("Перед викликом методу placeOrder()");
}
Приклад: After
@After("execution(* com.example.service.OrderService.placeOrder(..))")
public void logAfterPlaceOrder() {
System.out.println("Після виклику методу placeOrder()");
}
Приклад: Around
@Around("execution(* com.example.service.OrderService.placeOrder(..))")
public Object logAroundPlaceOrder(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("До виклику методу: " + joinPoint.getSignature().getName());
Object result = joinPoint.proceed(); // Виклик методу
System.out.println("Після виклику методу");
return result;
}
Чому це працює?
AOP в Spring базується на використанні проксі-об'єктів. Коли ви викликаєте метод, Spring підміняє вашу реалізацію на проксі, який перевіряє всі аспекти, пов'язані з цим методом. Якщо метод підходить під поінткат, Spring виконує пов'язаний з ним адвайс.
Корисні зауваження:
- Крос-секційні задачі відмінно вирішуються AOP. Типові приклади:
- Логування.
- Керування транзакціями.
- Перевірка безпеки.
- Вимірювання часу виконання.
- Не зловживайте AOP! Ваші аспекти повинні бути легкими і зрозумілими.
- Якщо логіка стає складною, можливо, аспекти — не найкраще рішення.
Таблиця: Зведення термінів AOP
| Термін | Опис | Приклад |
|---|---|---|
| Аспект | Модуль, що містить поведінку для крос-секційних задач | Логування викликів методів |
| Поінткат | Визначає, де має бути виконаний аспект | execution(* com.example.service.*.*(..)) |
| Адвайс | Конкретна дія, що виконується в обраних точках з'єднання | Логування перед викликом методу (@Before) |
Тепер ви розумієте, як аспекти (модулі) застосовують свої дії (адвайси) у визначених точках (поінткатах). Ми розібралися з базовими поняттями AOP. У наступній лекції ми побачимо, як застосувати ці знання на практиці для рішення реальних задач — логування, безпеки і керування транзакціями. А поки важливо зрозуміти, що AOP — це не просто магія Spring, а ретельно продуманий механізм з чіткими правилами виконання.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ