1. Обзор
В этом руководстве мы обсудим язык выражений pointcut Spring AOP.
Сначала мы введем некоторую терминологию, используемую в аспектно-ориентированном программировании. Точка соединения
— это шаг выполнения программы, такой как выполнение метода или обработка исключения. В Spring AOP точка соединения всегда представляет выполнение метода. Pointcut — это предикат, который соответствует точкам соединения, а язык выражений pointcut
—
это способ программного описания pointcut. ``
2. Использование
Выражение pointcut может отображаться как значение аннотации @Pointcut
:
@Pointcut("within(@org.springframework.stereotype.Repository *)")
public void repositoryClassMethods() {}
Объявление метода называется сигнатурой pointcut . Он предоставляет имя, которое может использоваться аннотациями-советами для ссылки на этот pointcut.
@Around("repositoryClassMethods()")
public Object measureMethodExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
...
}
Выражение pointcut также может отображаться как значение свойства expression
тега aop:pointcut :
<aop:config>
<aop:pointcut id="anyDaoMethod"
expression="@target(org.springframework.stereotype.Repository)"/>
</aop:config>
3. Маркеры Pointcut
Выражение pointcut начинается с указателя pointcut (PCD) , который является ключевым словом, сообщающим Spring AOP, что нужно сопоставлять. Существует несколько указателей pointcut, таких как выполнение метода, тип, аргументы метода или аннотации.
3.1. исполнение
Первичный PCD Spring — это execute , который соответствует точкам соединения выполнения метода .
@Pointcut("execution(public String com.foreach.pointcutadvice.dao.FooDao.findById(Long))")
Этот пример pointcut будет точно соответствовать выполнению метода findById
класса FooDao .
Это работает, но не очень гибко. Предположим, мы хотим сопоставить все методы класса FooDao
, которые могут иметь разные сигнатуры, типы возвращаемых значений и аргументы. Для этого мы можем использовать подстановочные знаки:
@Pointcut("execution(* com.foreach.pointcutadvice.dao.FooDao.*(..))")
Здесь первый подстановочный знак соответствует любому возвращаемому значению, второй соответствует любому имени метода, а шаблон (..)
соответствует любому количеству параметров (ноль или более).
3.2. в пределах
Другой способ добиться того же результата, что и в предыдущем разделе, — использовать внутри
PCD, который ограничивает сопоставление точками соединения определенных типов.
@Pointcut("within(com.foreach.pointcutadvice.dao.FooDao)")
Мы также можем сопоставить любой тип в пакете com.foreach или его подпакете.
@Pointcut("within(com.foreach..*)")
3.3. это
и цель
это
ограничивает сопоставление точками соединения, где ссылка на компонент является экземпляром данного типа, в то время как цель
ограничивает сопоставление точками соединения, где целевой объект является экземпляром данного типа. Первый работает, когда Spring AOP создает прокси-сервер на основе CGLIB, а второй используется при создании прокси-сервера на основе JDK. Предположим, что целевой класс реализует интерфейс:
public class FooDao implements BarDao {
...
}
В этом случае Spring AOP будет использовать прокси-сервер на основе JDK, и вы должны использовать целевой
PCD, поскольку проксируемый объект будет экземпляром класса Proxy
и реализует интерфейс BarDao
:
@Pointcut("target(com.foreach.pointcutadvice.dao.BarDao)")
С другой стороны, если FooDao
не реализует какой-либо интерфейс или для свойства proxyTargetClass
установлено значение true, то проксируемый объект будет подклассом FooDao,
и можно использовать этот
PCD:
@Pointcut("this(com.foreach.pointcutadvice.dao.FooDao)")
3.4. аргументы
Этот PCD используется для сопоставления конкретных аргументов метода:
@Pointcut("execution(* *..find*(Long))")
Этот pointcut соответствует любому методу, который начинается с find и имеет только один параметр типа Long
. Если мы хотим сопоставить метод с любым количеством параметров, но с первым параметром типа Long
, мы могли бы использовать следующее выражение:
@Pointcut("execution(* *..find*(Long,..))")
3.5. @цель
@target PCD
(не путать с целевым
PCD, описанным выше) ограничивает сопоставление точками соединения, где класс исполняемого объекта имеет аннотацию данного типа:
@Pointcut("@target(org.springframework.stereotype.Repository)")
3.6. @args
Этот PCD ограничивает сопоставление точками соединения, в которых тип времени выполнения фактически переданных аргументов имеет аннотации данного типа (типов). Предположим, мы хотим отследить все методы, принимающие бины, аннотированные аннотацией @Entity
:
@Pointcut("@args(com.foreach.pointcutadvice.annotations.Entity)")
public void methodsAcceptingEntities() {}
Чтобы получить доступ к аргументу, мы должны предоставить аргумент JoinPoint
совету:
@Before("methodsAcceptingEntities()")
public void logMethodAcceptionEntityAnnotatedBean(JoinPoint jp) {
logger.info("Accepting beans with @Entity annotation: " + jp.getArgs()[0]);
}
3.7. @в пределах
Этот PCD ограничивает сопоставление точками соединения внутри типов, имеющих данную аннотацию:
@Pointcut("@within(org.springframework.stereotype.Repository)")
Что эквивалентно:
@Pointcut("within(@org.springframework.stereotype.Repository *)")
3.8. @аннотация
Этот PCD ограничивает сопоставление точками соединения, где субъект точки соединения имеет заданную аннотацию. Например, мы можем создать аннотацию @Loggable
:
@Pointcut("@annotation(com.foreach.pointcutadvice.annotations.Loggable)")
public void loggableMethods() {}
Затем мы можем зарегистрировать выполнение методов, отмеченных этой аннотацией:
@Before("loggableMethods()")
public void logMethod(JoinPoint jp) {
String methodName = jp.getSignature().getName();
logger.info("Executing method: " + methodName);
}
4. Объединение выражений Pointcut
Выражения Pointcut можно комбинировать с помощью && , || и ! операторы:
@Pointcut("@target(org.springframework.stereotype.Repository)")
public void repositoryMethods() {}
@Pointcut("execution(* *..create*(Long,..))")
public void firstLongParamMethods() {}
@Pointcut("repositoryMethods() && firstLongParamMethods()")
public void entityCreationMethods() {}
5. Вывод
В этом кратком введении в Spring AOP и pointcut мы проиллюстрировали несколько примеров использования выражений pointcut.
Полный набор примеров можно найти на GitHub .