Слайд 2План
Pointcut
Комбинирование Pointcut
Порядок выполнения Aspect-ов
Слайд 3Pointcut
Pointcut – выражение, описывающее, где должен быть применен Advice.
Spring AOP использует AspectJ
Pointcut expression language, то есть определенные правила в написании выражений для создания Pointcut.
Слайд 4Pointcut
Для описания Pointcut используется шаблон. Выделены обязательные элементы.
execution( modifiers-pattern? return-type-pattern declaring-type-pattern? method-name-pattern(parameters-pattern)
throws-pattern? )
Под шаблон может подходить один или несколько методов, в зависимости от того, насколько детально был описан шаблон.
Слайд 5Pointcut
Рассмотрим пример с предыдущей лекции.
По шаблону мы указали все, кроме declaring-type-pattern и
throws-pattern. Отсутствие declaring-type-pattern означает, что под шаблон подойдет метод getBook() абсолютно любого класса.
Слайд 6Pointcut
Создадим абстрактный класс
от которого будет наследоваться класс Library
Слайд 7Pointcut
Переименуем класс, чтобы его название конкретизировало его природу. Например, в университетскую библиотеку.
Создадим
класс школьная библиотека.
Как вы можете видеть, под шаблон подходит оба метода.
Слайд 8Pointcut
Вызовем метод в классе Test1
Слайд 9Pointcut
Вывод:
Это произошло, поскольку оба метода подходят под Pointcut.
Слайд 10Pointcut
Модифицируем Pointcut, чтобы метод вызвался только для UniLibrary. Для этого нам необходимо
указать declaring-type-pattern, а именно полное имя класса UniLibrary.
Вывод:
Слайд 11Pointcut
execution(public void getBook()) – соответствует методу без параметров, где бы он ни
находился, с модификатором доступа public, возвращаемым типом void и названием getBook()
execution(public void com.donnu.demo.aop.UniLibrary.getBook()) – соответствует методу без параметров, из класса UniLibrary, с модификатором доступа public, возвращаемым типом void и названием getBook()
Слайд 12Pointcut
execution(public void get*()) – соответствует методу без параметров, где бы он ни
находился, с модификатором доступа public, возвращаемым типом void и названием, начинающимся на get.
Слайд 13Pointcut
Добавим метод getMagazine в UniLibrary.
Слайд 14Pointcut
Добавим вызов метода getMagazine в Test1.
Слайд 15Pointcut
Как мы можем видеть аспект был вызван трижды. Для getBook и getMagazine
из UniLibrary и getBook из SchoolLibrary. Все эти методы подошли под шаблон.
Слайд 16Pointcut
Приведем пример работы с return-type-pattern. Добавим еще один Advice:
и метод в UniLibrary:
Слайд 17Pointcut
В классе Test1 вызовем метод returnBook:
Вывод указывает на то, что Advice работает:
Слайд 18Pointcut
Теперь, если мы изменим возвращаемый тип, например вот так:
Метод больше не будет
подходить под шаблон, Advice не будет вызван. Если же мы не хотим зависеть от возвращаемого типа, а он является обязательным параметром шаблона, мы можем изменить возвращаемый тип на *:
Слайд 19Pointcut
Если мы хотим аналогичным образом поступить с модификатором доступа, мы можем просто
его убрать, оставив только *.
Как вы помните modifiers-pattern не является обязательным элементом шаблона.
Слайд 20Pointcut
Таким образом:
execution(* returnBook()) - соответствует методу без параметров, где бы он ни
находился, с любым модификатором доступа, любым возвращаемым типом и названием returnBook()
execution(* *()) - соответствует методу без параметров, где бы он ни находился, с любым модификатором доступа, любым возвращаемым типом и любым называнием
Слайд 21Pointcut
Рассмотрим параметры метода при написании Pointcut. Для этого вернем класс UniLibrary в
первоначальное состояние.
Это необходимо для более простой работы с добавлением параметров.
Слайд 22Pointcut
Добавим параметр в метод getBook
В Test1 добавим параметр при вызове метода
Убедимся, что
при этом наш Advice больше не срабатывает
Слайд 23Pointcut
Добавим параметр в Advice. Обратите внимание, что указывается только тип параметра, но
не его название.
Вывод:
Слайд 24Pointcut
execution(public void getBook(String)) - соответствует методу с параметром String, где бы он
ни находился, с модификатором доступа public, возвращаемым типом void и названием getBook()
Слайд 25Pointcut
Допустим, что мы хотим, чтобы под наш шаблон подходил любой метод, имеющий
только 1 параметр. Для этого добавляем параметр в getMagazine:
А сам шаблон модифицируем следующим образом:
Слайд 26Pointcut
Вызовем метод в Test1:
Вывод:
Слайд 27Pointcut
Если же мы хотим, чтобы под описанный шаблон подходил метод с любым
количеством параметров, необходимо * заменить на ..
Это будет работать даже в том случае, если параметров 0
Слайд 28Pointcut
execution(public void getBook(String)) - соответствует методу с параметром String, где бы он
ни находился, с модификатором доступа public, возвращаемым типом void и названием getBook()
execution(public void getBook(*)) - соответствует методу с любым одним параметром, где бы он ни находился, с модификатором доступа public, возвращаемым типом void и названием getBook()
execution(public void getBook(..)) - соответствует методу с любым количеством любого типа параметров, где бы он ни находился, с модификатором доступа public, возвращаемым типом void и названием getBook()
Слайд 29Pointcut
Рассмотрим ситуацию, когда параметр имеет тип созданного нами класса. Создадим класс Book:
Слайд 30Pointcut
В UniLibrary меняем параметр метода getBook на Book.
В Test1 получаем объект класса
Book и передаем в метод getBook.
Слайд 31Pointcut
Но, если в параметре Advice мы напишем просто Book будет ошибка.
Нашему Pointcut
непонятно, о каком типе идет речь. Необходимо указать полное имя класса.
Слайд 32Pointcut
execution(public void getBook(com.donnu.demo.aop.Book, ..)) - соответствует методу с первым параметром Book, и
любым количеством других параметров, даже 0, где бы он ни находился, с модификатором доступа public, возвращаемым типом void и названием getBook()
execution(* *(..)) - соответствует методу с любым количеством других параметров любого типа, где бы он ни находился, с любым модификатором доступа, любым возвращаемым типом и любым названием
Слайд 33Pointcut
Вернем UniLibrary и другие элементы в исходное состояние
Слайд 34Pointcut
Изменим Aspect, чтобы реализовать в нем и Advice для проверки прав.
Слайд 35Pointcut
Проверим работу Advice.
Логика работы beforeGetSecurityAdvice будет аналогична beforeGetLoggingAdvice:
Слайд 36Pointcut
Для того, чтобы не пользоваться копированием, когда для нескольких Advice подходит один
и тот же Pointcut, есть возможность объявить Pointcut, а потом использовать его несколько раз.
@Pointcut(“pointcut_expression”)
private void pointcut_reference(){}
Использование:
@Before(“pointcut_reference()”)
public void advice_name(){ /*code*/}
Слайд 37Pointcut
В нашем примере будет выглядеть следующим образом:
Слайд 38Pointcut
Если данный метод будет иметь модификатор доступа public, то мы сможем использовать
его в других классах аспектах.
@Pointcut(“pointcut_expression”)
public void pointcut_reference(){}
Слайд 39Pointcut
Плюсы объявления Pointcut:
Возможность использования одного Pointcut для множества Advice
Возможность быстрого изменения Pointcut
для множества Advice
Возможность комбинирования Pointcut
Слайд 40Комбинирование Pointcut
Добавим в класс UniLibrary несколько методов
Слайд 41Комбинирование Pointcut
Добавим в LoggingAndSecurityAspect добавим метод логирования получения книг
Слайд 42Комбинирование Pointcut
Аналогично для return-методов. Но, что если у нас есть сквозная логика,
которая должна выполняться в обоих случаях?
Слайд 43Комбинирование Pointcut
Комбинирование Pointcut-ов – это их объединение с помощью логических операторов &&
|| !
Слайд 45Комбинирование Pointcut
Запустим Test1
Слайд 46Комбинирование Pointcut
Рассмотрим ситуацию, когда мы хотим вызвать Advice для всех методов кроме
одного.
Слайд 47Комбинирование Pointcut
Вызываем Test1
Слайд 48Порядок выполнения Aspect-ов
Рассмотрим ранее написанный пример.
Слайд 49Порядок выполнения Aspect-ов
В Test1 вызовем методы с get.
Вывод:
Слайд 50Порядок выполнения Aspect-ов
Каким образом мы можем контролировать порядок выполнения?
Для этого нам потребуется
вынести методы в разные аспекты. Но начнем с того, что вынесем Pointcut в отдельный класс.
Установим модификатор доступа public, чтобы мы могли к нему обратиться из другого класса.
Слайд 51Порядок выполнения Aspect-ов
Теперь создадим два аспекта. Обратите внимание, что для того, чтобы
получить Pointcut необходимо указать полное имя класса.
Слайд 52Порядок выполнения Aspect-ов
Создадим еще один аспект.
Теперь у нас три аспект-класса и три
Advice направленных на get-метод.
Слайд 53Порядок выполнения Aspect-ов
Теперь мы можем указать им порядок, с помощью аннотации @Order
Слайд 54Порядок выполнения Aspect-ов
Вывод до аннотации @Order:
Вывод после:
Слайд 55Порядок выполнения Aspect-ов
Если при вызове одного метода с бизнес-логикой срабатывает несколько Advice,
то нет никакой гарантии, что они выполнятся в желаемом порядке.
Для соблюдении порядка такие Advice необходимо распределить по отдельным, упорядоченным аспектам.
Аннотация @Order(1) упорядочивает аспекты. Чем меньше число, тем выше приоритет. Число должно быть целым, отрицательное допустимо.