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 мощный инструмент, но использовать его следует с осторожностью. Внедрение его в уже действующий код может быть дорогим, так что, лучше заранее определиться нужен ли он?