Введение в выражения Pointcut весной

Введение в выражения Pointcut весной

1. обзор

В этом уроке мы обсудим язык выражений PointOp Spring AOP.

Сначала мы введем некоторую терминологию, используемую в аспектно-ориентированном программировании. join point - это шаг выполнения программы, такой как выполнение метода или обработка исключения. В Spring AOP точка соединения всегда представляет выполнение метода. pointcut - это предикат, который соответствует точкам соединения, аpointcut expression language - это способ программного описания pointcut.

2. использование

Выражение pointcut может отображаться как значение аннотации@Pointcut:

@Pointcut("within(@org.springframework.stereotype.Repository *)")
public void repositoryClassMethods() {}

Объявление метода называетсяpointcut signature. Он предоставляет имя, которое может использоваться аннотациями советов для ссылки на этот pointcut.

@Around("repositoryClassMethods()")
public Object measureMethodExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
    ...
}

Выражение pointcut также может отображаться как значение свойстваexpression тегаaop:pointcut:


    

3. Обозначения Pointcut

Выражение pointcut начинается сpointcut designator (PCD), которое является ключевым словом, сообщающим Spring AOP, чему соответствовать. Существует несколько обозначений точек, таких как выполнение метода, типа, аргументов метода или аннотаций.

3.1 execution

Основным Spring PCD являетсяexecution, который соответствует точкам соединения выполнения метода.

@Pointcut("execution(public String org.example.dao.FooDao.findById(Long))")

Этот пример pointcut будет точно соответствовать выполнению методаfindById классаFooDao. Это работает, но это не очень гибко. Предположим, мы хотели бы сопоставить все методы классаFooDao, которые могут иметь разные сигнатуры, возвращаемые типы и аргументы. Для этого мы можем использовать подстановочные знаки:

@Pointcut("execution(* org.example.dao.FooDao.*(..))")

Здесь первый подстановочный знак соответствует любому возвращаемому значению, второй соответствует любому имени метода, а шаблон(..) соответствует любому количеству параметров (от нуля или более).

3.2 within

Другой способ добиться того же результата из предыдущего раздела - использовать PCDwithin, который ограничивает сопоставление точками соединения определенных типов.

@Pointcut("within(org.example.dao.FooDao)")

Мы также можем сопоставить любой тип в пакетеorg.example или подпакете.

@Pointcut("within(org.example..*)")

3.3 this and target

this ограничивает сопоставление точками соединения, в которых ссылка на компонент является экземпляром данного типа, в то время какtarget ограничивает сопоставление точками соединения, где целевой объект является экземпляром данного типа. Первый работает, когда Spring AOP создает прокси на основе CGLIB, а второй используется при создании прокси на основе JDK. Предположим, что целевой класс реализует интерфейс:

public class FooDao implements BarDao {
    ...
}

В этом случае Spring AOP будет использовать прокси на основе JDK, и вам следует использовать PCDtarget, потому что проксируемый объект будет экземпляром классаProxy и будет реализовывать интерфейсBarDao:

@Pointcut("target(org.example.dao.BarDao)")

С другой стороны, еслиFooDao не реализует какой-либо интерфейс или для свойстваproxyTargetClass установлено значение true, то проксируемый объект будет подклассомFooDao, и PCDthis может использоваться:

@Pointcut("this(org.example.dao.FooDao)")

3.4 args

Этот PCD используется для сопоставления определенных аргументов метода:

@Pointcut("execution(* *..find*(Long))")

Этот pointcut соответствует любому методу, который начинается с find и имеет только один параметр типаLong. Если мы хотим сопоставить метод с любым количеством параметров, но с первым параметром типаLong, мы могли бы использовать следующее выражение:

@Pointcut("execution(* *..find*(Long,..))")

3.5 @target

PCD@target (не путать с PCDtarget, описанным выше) ограничивает сопоставление точками соединения, в которых класс исполняемого объекта имеет аннотацию данного типа:

@Pointcut("@target(org.springframework.stereotype.Repository)")

3.6 @args

Этот PCD ограничивает соответствие точками соединения, где тип времени выполнения переданных фактических аргументов имеет аннотации данного типа (типов). Предположим, что мы хотим отследить все методы, принимающие bean-компоненты, аннотированные аннотацией@Entity:

@Pointcut("@args(org.example.aop.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 @within

Этот PCD ограничивает соответствие для точек соединения в типах, которые имеют данную аннотацию:

@Pointcut("@within(org.springframework.stereotype.Repository)")

Что эквивалентно:

@Pointcut("within(@org.springframework.stereotype.Repository *)")

3.8 @annotation

Этот PCD ограничивает соответствие точками соединения, в которых объект точки соединения имеет заданную аннотацию. Например, мы можем создать аннотацию@Loggable:

@Pointcut("@annotation(org.example.aop.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 и pointcuts мы проиллюстрировали несколько примеров использования выражений pointcut.

Полный набор примеров можно найти вmy github project - это проект на основе Eclipse, поэтому его должно быть легко импортировать и запускать как есть.