1.概述
本文主要记录一下 Spring AOP 中 Pointcut 切点表达式。
关于 AOP 及切点相关的概念可以参考 Spring源码解读——AOP。
2. 用法
在基于注解的 AOP 开发过程中,切点表达式主要用于 @Pointcut 注释的值:
1 | "within(@org.springframework.stereotype.Repository *)") ( |
方法声明称为切入点签名,它定义了一个切点范围,后续在创建 Advice 时可以直接引用这个签名,来确定 Advice 的拦截范围。
1 | "repositoryClassMethods()") ( |
也可以通过 XML aop:pointcut 配置的方式定义:
1 | <aop:config> |
3. 切入点指示符
切入点表达式以pointcut designator(PCD)开头,告诉Spring AOP要匹配的关键字。下面将介绍常用的指示符,例如方法的执行,类型,方法参数或注解。
3.1 execution
execution 是 Spring AOP 中最主要也是最常用的 PCD,它匹配方法执行点:
1 | "execution(public String com.doleje.dao.FooDao.findById(Long))") ( |
此示例切入点将严格匹配FooDao类的findById方法的执行。如果我们想更灵活的控制,比如匹配FooDao类的所有方法,它们可能具有不同的签名,返回类型和参数,那么还可以使用通配符:
1 | "execution(* com.doleje.dao.FooDao.*(..))") ( |
这里第一个 * 匹配任何返回值,第二个 * 任何方法名称,*(..)* 模式匹配任意数量的参数(零或更多)。
3.2 within
within 用于匹配某个类或者包内的所有操作,比如我们要实现上一节中的匹配 FooDao 中所有方法还可以用如下的方式:
1 | "within(com.doleje.dao.FooDao)") ( |
我们也可以匹配com.doleje 包或子包中的任何类型。
1 | "within(com.doleje..*)") ( |
3.3 this 和 target
this 切点函数则通过判断 代理类 是否按类型匹配指定类来决定是否和切点匹配。 用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配。 this中使用的表达式必须是类型全限定名,不支持通配符。
target 匹配目标对象的类型,即被代理对象的类型,例如A继承了B接口,则使用target(“B”),target(“A”) 均可以匹配到A(因为 A 也是 B 接口的目标对象)。
这两个概念确实比较绕,也很容易弄混。但简单的,你可以记住如下的规则,假设目标类实现了一个接口:
1 | public class FooDao implements BarDao { |
由于接口的存在,在这种情况下,Spring AOP将使用基于JDK的动态代理技术,你应该使用target ,因为代理对象将是Proxy类的实例并实现BarDao接口:
1 | "target(com.doleje.dao.BarDao)") ( |
另一方面,如果FooDao没有实现任何接口或者 proxyTargetClass 属性设置为true,则代理对象将是FooDao的子类,并且可以使用 this:
1 | "this(com.doleje.dao.FooDao)") ( |
3.4 args
args 用于匹配特定方法参数:
1 | "execution(* *..find*(Long))") ( |
此切入点匹配以find开头的任何方法,并且只有一个Long类型的参数。如果我们想要一个方法与任意数量的参数匹配但是具有Long类型的第一个参数,我们可以使用以下表达式:
1 | "execution(* *..find*(Long,..))") ( |
3.5 @target
@target 用于匹配某些有特定注解的类,注意,不要与 target 混淆
1 | `@Pointcut``(``"@target(org.springframework.stereotype.Repository)"``)` |
3.6 @args
@args 匹配任何一个只接受一个参数的方法,且方法运行时传入的参数持有该注解的点。
1 | "@args(com.doleje.aop.annotations.Entity)") ( |
可以通过 Advice 提供的 JoinPoint 来访问具体的参数以及注解:
1 | "methodsAcceptingEntities()") ( |
3.7 @within
@within 将匹配具有给定注解的类型中的连接点:
1 | "@within(org.springframework.stereotype.Repository)") ( |
这相当于:
1 | "within(@org.springframework.stereotype.Repository *)") ( |
3.8 @annotation
@annotation 将匹配添加了某个注解的方法,比如我们定义一个 @Loggable 注解:
1 | (RetentionPolicy.RUNTIME) |
然后针对有这个注解的方法添加一个切点:1
2"@annotation(com.doleje.aop.annotations.Loggable)") (
public void loggableMethods() {}
最后我们写一个 Advice 来对匹配到这个切点的方法进行一些切面处理:
1 | "loggableMethods()") ( |
4. 切入点表达式的组合
Pointcut expressions 是可以使用&&,|| 以及 ! 来组合的:
1 | "@target(org.springframework.stereotype.Repository)") ( |