В прошлой лекции мы познакомились с базовыми концепциями 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, а тщательно продуманный механизм с чёткими правилами выполнения.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ