Перейти к основному содержимому

Введение в выражения Pointcut в Spring

· 4 мин. чтения

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 .