Spring: AOP
Основной задачей является устранение проблемы вызванной code tangling (запутанность) и code scattering (разбросаность). Tangling - запутанность бизнес логики и сложность технической реализаций; например, управление БД и бизнес взаимодействие с данными в ней в одном месте; Scattering - распределение/дублирование кода по в разных частях.
Для работы с AOP, Spring предалагает следующие библиотеки:
- spring-aop - базовый API, включая компоненты для перехвата событий;
- spring-aspect - интеграция с AspectJ;
- spring-instrument - библиотека для интеграции с серверами приложений.
AOP понятия
- Aspect - класс, содержащий механизм AOP. Для Spring - любой аннотированный
@Aspec; - Weaving - связь между AOP объектом и другими объектами, реализациями или изменением поведения;
- Join Point - точка использования AOP. Для Spring это метод выполнения;
- Target Object - объект под воздействием AOP;
- Target Method - метод под воздействием AOP;
- Advice - действие, выполняемое в Join Point. Бывает:
- Before Advice - метод, аннотированный
@Beforeи который будет выполнен до Joint Point. Не мешает выполнению Target Method; - After Returning Advice - аннотирован
@AfterReturningи выполняемый после “нормального” выполнения Target Method (безException); - After Throwing Advice - обратен After Returning Advice, выполняется после выбрасывания исключения (
Exception). Аннотация@AfterThrowing; - After (finally) Advice - аннотирован
@Afterи выполняется всегда после Target Method (вне заувисимости было исключение или нет); - Around Advice - аннотирован
@Around. Позволяет выбрать поведение - выполнить Target Method? К объекту Target Object? Выбросить исключение? Вернуть результат? Или выбросить исключение?
- Before Advice - метод, аннотированный
- Pointcut - указатель, идентифицирующий Join Point. Определяется посредством AspectJ Pointcut Expression Language либо в одной из аннотаций Advice группы (
@{advice}, см. выше), либо в специальной аннотации@Pointcut; - Introduction - внедрение кода в Target Object. Использует
@Declare*для внедрения методов, переменных и т.д.; - AOP Proxy - прокси объекты, которые служат обертками для Target Object, реализующими AOP
Для возможности использования AOP в Spring потребуется:
- Наличие spring-aop и aspectweaver библиотек;
- Создать бин (через
@Component,@Bean, или иными способами) и добавить к нему аннотацию@Aspect; - Определить Advice Method -
@Before,@After, …; - В
@Configurationдобавить включение AOP:@EnableAspectJAutoProxy. Возможно, с параметромproxyTargetClass=true;
AOP Expression Language
Шаблон для выражения AOP следующий:
1 | execution_method([modifiers] [return_type] [class].[method]([arguments...]) throws [exception_class]) |
Некоторые особенности построения шаблонов:
- Для класса можно использовать wildcard+, при которой поиск будет соответствовать указанному классу, а так же всем его подклассам;
- Использование wildcard* осуществляет поиск даже среди произвольных символов: например,
findBy*гарантирует нахождениеfindById,findBy_FirstName,findBy1st_name; - Ограничение
within(com.mycompany.package.*)позволит ограничить поиск только одним пакетом; - Ограничение
@annotation(com.mycompany.annotations.Limited)позволяет ограничивать поиск по аннотации; - Модификаторы доступа [modifiers] являются необязательными и в случае отсутствия заменяются на
public; - Допустимо использование булевых логических условий типа
&&и||. Например:execution(*.*MyRepo+.findBy*(...)) && within(my.company.pack).
Вынесенные Pointcuts
Иногда выражение может быть очень сложным:
1 | execution_method(* com.mycompany.*.*.MyRepo+.findBy*(...)) || execution(* com.mycompany.objects.dao.*.*Service+.findBy*(String)) |
Такое выражение сложно прочитать, а так же оно является сложно переносимым (т.е. придется дублировать либо его целиком, либо какие-то части). Аннотация @Pointcut позволяет избежать подобных проблем:
1 |
|
В Advice аннотации (@Before) в примере выше можно указать класс, из которого следует брать Pointcut.
JoinPoint
Для получения полезной информации из Join Point можно:
1 | Class targetClass = point.getSignature().getDeclatingType(); |
Можно потребовать преобразовать и вытащить атрибуты по именам (требуется полное соответствие с сигнатурой):
1 |
|
Важно помнить, что args(...) применимо только к одному параметру. Если в методе их больше придется вытаскивать через getArgs().
Advice и их виды
@Before. Выполняется перед Target Method. В случае ошибки выполнения Advice, этот экземплярExceptionпрокинется вызывающему методу, а Target Method вызван не будет;@AfterReturning. Вызывается после успешного выполнения Target Method (безException). Может принимать параметрreturning, в котором можно указать имя переменной, в которую следует передать результат выполнения Target Method:
1 |
|
@AfterThrowing. Обратный предыдущему - выполняется только если Target Method выполнится с ошибкой:
1 |
|
Метод не(!) останавливается при ошибке, но благодаря throw new CustomException позволяет изменить выбрасываемое исключение (определяется в теле метода, аннотированного @AfterThrowing);
@After. Выполняется всегда, будь то Target Method завершен успешно, или выполнен с ошибкой;@Around. Единственный Advice, который может повлиять на выполнение Target Method. Может полностью поменять поведение метода
1 |
|
Итог
Spring AOP мощный инструмент, но использовать его следует с осторожностью. Внедрение его в уже действующий код может быть дорогим, так что, лучше заранее определиться нужен ли он?