JavaRush /Курси /Модуль 5. Spring /Занурюємося в AOP: детальний розбір аспектів, поінткатів ...

Занурюємося в AOP: детальний розбір аспектів, поінткатів і адвайсів

Модуль 5. Spring
Рівень 3 , Лекція 1
Відкрита

У попередній лекції ми познайомилися з базовими концепціями AOP. Але між "знати терміни" і "розуміти, як це працює" — велика різниця. Давайте зазирнемо під капот і розберемося в деталях.

1. Аспект: не просто модуль

Ми вже знаємо, що аспект — це модуль для крос-секційних задач. Але як Spring його створює і керує ним?

Життєвий цикл аспекту

Аспект в Spring - це не просто клас з анотацією @Aspect. Це повноцінний резидент Spring-контейнера зі своїм життєвим циклом:

  1. Ініціалізація: Spring створює аспекти на самому ранньому етапі завантаження контексту
  2. Проксування: на основі аспектів створюються проксі для цільових бінів
  3. Впровадження: аспект може використовувати інші біни через @Autowired
  4. Виконання: Spring викликає методи аспекту при спрацьовуванні тригерів

Особливості роботи аспектів

На відміну від звичайних бінів, аспекти мають кілька важливих особливостей:

  1. Пріоритизація: коли кілька аспектів націлені на один метод, порядок їх виконання критичний:
    
    @Aspect
    @Order(1)  // Спочатку перевіримо права
    public class SecurityAspect { ... }
    
    @Aspect
    @Order(2)  // Потім запишемо в лог
    public class LoggingAspect { ... }
    
  2. Самозастосування: за замовчуванням аспекти не застосовуються до методів всередині самих аспектів — це захист від рекурсії

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 виконує пов'язаний з ним адвайс.


Корисні зауваження:

  1. Крос-секційні задачі відмінно вирішуються AOP. Типові приклади:
    • Логування.
    • Керування транзакціями.
    • Перевірка безпеки.
    • Вимірювання часу виконання.
  2. Не зловживайте AOP! Ваші аспекти повинні бути легкими і зрозумілими.
  3. Якщо логіка стає складною, можливо, аспекти — не найкраще рішення.

Таблиця: Зведення термінів AOP

Термін Опис Приклад
Аспект Модуль, що містить поведінку для крос-секційних задач Логування викликів методів
Поінткат Визначає, де має бути виконаний аспект execution(* com.example.service.*.*(..))
Адвайс Конкретна дія, що виконується в обраних точках з'єднання Логування перед викликом методу (@Before)

Тепер ви розумієте, як аспекти (модулі) застосовують свої дії (адвайси) у визначених точках (поінткатах). Ми розібралися з базовими поняттями AOP. У наступній лекції ми побачимо, як застосувати ці знання на практиці для рішення реальних задач — логування, безпеки і керування транзакціями. А поки важливо зрозуміти, що AOP — це не просто магія Spring, а ретельно продуманий механізм з чіткими правилами виконання.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ