В этом блоге рассматриваются основы аспектно-ориентированного программирования (АОП). Я покажу простой рабочий пример АОП: обычная задача, такая как регистрация вызовов REST, может быть в общем определена в Совете, который затем может быть применен к различным целевым местам в нашем коде (так называемый Pointcuts) через аспект.

Для нетерпеливых: вы можете найти конечный результат в моем репозитории на Github:



Парадигмы программирования

Еще в 1996 году, когда была выпущена Java 1.0, все Java-разработчики были увлечены объектно-ориентированным программированием, также называемым ООП. Хотя ООП был одним из основополагающих драйверов для Java, сам язык программирования с тех пор прошел долгий путь и теперь поддерживает множество парадигм программирования.

Вот список основных парадигм программирования, поддерживаемых в Java (в историческом порядке):

  • Процедурное программирование
  • Объектно-ориентированное программирование (ООП)
  • Функциональное программирование
  • Аспектно-ориентированное программирование (АОП)

В этом посте мы сосредоточимся на АОП и покажем, как создавать аспекты. Вы узнаете как об основах АОП, так и о том, как использовать его с SpringBoot.

Почему АОП?

У большинства крупных компаний есть руководящие принципы программирования, и у меня тоже. Одно из наших правил гласит, что каждое выполнение конечной точки REST должно регистрироваться (имя метода Java + параметры).

Вот как вы могли бы решить эту проблему:

Приведенный выше фрагмент кода выполняет следующие действия:

  • @RestController: Убедитесь, что SpringBoot знает, что этот класс содержит конечные точки REST
  • @GetMapping: метод, отвечающий на запросы HTTP GET.
  • System.out.println(...): соблюдайте вышеупомянутые правила кодирования.
  • return значение: метод просто возвращает приветственное сообщение типа String

В реалистичном приложении у вас будет много таких вызовов REST во многих разных классах. Вести одно и то же ведение журнала всеми этими методами громоздко. Более того, если правила кодирования немного изменятся, вам придется изменить сообщение журнала в каждом методе.

Здесь на помощь приходит АОП: с помощью АОП мы можем красиво добавить общие функции во многие различные места нашего кода, не вмешиваясь в существующий код. На литературном жаргоне АОП означает разделение сквозных задач. На более понятном для человека языке АОП позволяет модулировать общие задачи по разным объектам.

Зависимость от Maven

Чтобы начать использовать АОП в SpringBoot с аннотациями AspectJ, нам нужно импортировать следующие зависимости в наш pom.xml:

Совет

Давайте создадим функцию, которая выполняет необходимое нам ведение журнала в общем виде:

Эта универсальная функция называется Совет. Обратите внимание, что он может регистрировать имя и параметры любого метода; давайте разберем совет шаг за шагом:

  • JoinPoint: этот объект содержит всю информацию о Точке соединения, то есть месте, куда будет вставлен наш Аспект. В нашем случае это будет метод REST, для которого мы хотим создать сообщение журнала.
  • joinPoint.getSignature() и joinPoint.getArgs() извлекает подпись метода, а также вызывающие аргументы
  • System.out.println(...): ведите необходимые журналы

Pointcut

Итак, где мы хотим вставить вышеуказанный метод Advise? Что ж, мы хотим, чтобы отслеживалась каждая конечная точка REST. Хотя есть много способов пометить наши конечные точки REST, мы решили использовать настраиваемую аннотацию для определения Pointcut:

Как видите, определение Pointcut является однострочным:

  • @Before: мы запускаем Advice до того, как будет получен ответ на вызов REST
  • @annotation: мы отмечаем Pointcuts с помощью аннотации
  • LogMethod: это название нашей пользовательской аннотации

Теперь мы готовы пометить наш метод REST нашей собственной аннотацией:

Обратите внимание, что мы добавили к методу REST аннотацию @LogMethod. Более того, мы удалили логирование внутри метода, теперь этим занимается наш Аспект.

Аспект

Аспект - это Pointcut плюс совет. Итак, давайте сложим их вместе, и мы получим:

Вот что у нас есть:

  • @Aspect: SpringBoot ожидает, что все аспекты будут в классах, аннотированных @Aspect
  • @Before(...): Pointcut
  • logMethodName(...){...}: Совет

Итак, все, что мы здесь сделали, это просто объединили ранее показанное выражение Pointcut с советом и обернули все в класс. Принесите шампанское, у нас есть Аспект, готовый и работающий 🥂

Включить AOP

В заключение, мы должны включить AspectJ для нашей конфигурации Spring:

Помните, что мы хотим иметь возможность работать с Beans при использовании Spring. На данный момент наш класс @RestController содержит только логику REST-вызова, но не наш Совет. Spring может создавать Прокси для таких Бинов, которые содержат эту дополнительную логику (Совет), и это разрешено @EnableAspectJAutoProxy.

Заключение

Это все! Теперь у вас есть полностью рабочий пример АОП😀💪🍾

Мы реализовали совет, который будет запускаться каждый раз, когда выполняется метод с аннотацией @LogMethod. Благодаря нашему подходу АОП мы можем добавить эту аннотацию к будущим методам REST, которые затем будут сообщаться с тем же аспектом!

Ознакомьтесь с полностью работающим примером на Github:



Это репозиторий Github также содержит второй совет типа @Around: каждый раз, когда вызывается метод REST, мы регистрируем время выполнения. Это может пригодиться для измерения, мониторинга и сравнения производительности различных конечных точек REST.

Спасибо за чтение, оставьте комментарий, если у вас есть отзывы или вопросы! 😀

Дальнейшие чтения

Для любопытных, вот еще несколько материалов для чтения:

Праймер AOP и типы советов:
В нашем примере мы использовали совет типа @Before. Как вариант, вы можете использовать @After, @Around или @AfterThrowing.

Pointcuts - это предикаты, соответствующие набору точек соединения. Мы использовали Pointcut на основе аннотаций, но Spring поддерживает гораздо больше типов обозначений. Например, Spring поддерживает execution Pointcuts, где имя метода должно соответствовать заданному шаблону.

Прокси AOP: объяснение в официальной документации Spring.

Первоначально опубликовано на Dev.to.