今天介绍 Pointcut 的表达式
通配符
常见的通配符如下
..
含义一:方法表达式中、代表任意数量的参数
@Service public class HelloService { public void sayHi(String name) { System.out.println("hi," + name); } public void sayHi(String firstName, String lastName) { System.out.println("hi," + firstName + lastName); } } @Pointcut("execution(public void com.example.junitspringboot.service.HelloService.sayHi(..))") public void pointcut(){} 复制代码
那么上面的 Pointcut 表达式就能将 HelloService 中的两个连接点都包括进去。
- 连接点:程序执行的某个特定位置,比如某个方法调用前、调用后,方法抛出异常后,对类成员的访问以及异常处理程序块的执行等。一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就是连接点。它自身还可以嵌套其他的 Joinpoint。AOP 中的 Joinpoint 可以有多种类型:构造方法调用,字段的设置和获取,方法的调用,方法的执行,异常的处理执行,类的初始化。Spring 仅支持方法执行类型的 Joinpoint。
含义二:类定义表达式中、代表任意子包
@Component @Aspect public class ServiceAop { @Pointcut("execution(public void com.example.junitspringboot..HelloService.sayHi(..))") public void pointcut(){} @Around("pointcut()") public Object before(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("before"); return proceedingJoinPoint.proceed(); } } // 注意包名 package com.example.junitspringboot.service.inner; import org.springframework.stereotype.Service; @Service("innerHelloService") public class HelloService { public void sayHi(String firstName, String lastName) { System.out.println("hi," + firstName + lastName); } } // 注意包名 package com.example.junitspringboot.service; public class HelloService { public void sayHi(String name) { System.out.println("hi," + name); } } 复制代码
+
匹配给定类及其子类
public interface Person { void say(); } @Service public class Man implements Person{ @Override public void say() { System.out.println("man"); } } @Service public class Woman implements Person { @Override public void say() { System.out.println("woman"); } } @Pointcut("within(com.example.junitspringboot.service.Person+)") public void pointcut(){} 复制代码
那么接口 Person 的子类所有实现的方法都会被增强。
*
匹配任意数量的字符
// com.example.junitspringboot.service 包所有的类的所有方法 @Pointcut("within(com.example.junitspringboot.service.*)") public void pointcut1(){} // 所有以 say 开头的方法 @Pointcut("execution(* say*(..))") public void pointcut2(){} 复制代码
execution
使用得最多的表达式、用于指定方法的执行。? 表示非必填
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) 复制代码
- modifiers-pattern? 表示修饰符、如 public、protected
- ret-type-pattern 返回类型、必填。 如果使用通配符 * 代表任意的返回类型
- declaring-type-pattern? 表示声明方法的类。
- name-pattern 表示方法名。
- param-pattern 表示方法参数、如果使用通配符 .. 则代表任意参数
- throws-pattern? 表示方法抛出的异常
例子
execution(public * com.example..say*(..)) 复制代码
- 修饰符为 public
- 任意返回类型
- 在 com.example 包或者其子包下
- 方法名称以 say 开头
- 任意参数
@Pointcut("execution( * say*(..))") 复制代码
- 任意返回类型
- 方法名称以 say 开头
- 任意参数
@Pointcut("execution(* *(..) throws Exception)") 复制代码
- 方法声明抛出 Exception 的任意方法
within
指定特定类型、类型中所有的方法都被拦截。
@Pointcut("within(com.example.junitspringboot.service.Person)") 复制代码
- Person 类所有外部的方法调用都被拦截
@Pointcut("within(com.example.junitspringboot.service.Person+)") 复制代码
- Person 类及其子类所有外部的方法调用都被拦截
@Pointcut("within(com.example.junitspringboot.service..*)" 复制代码
- 所有在 com.example.junitspringboot.service 包以及子包下的所有类的所有外部调用方法
this
this通过判断代理类是否按类型匹配指定类来决定是否和切点匹配。 用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配。 this中使用的表达式必须是类型全限定名,不支持通配符。
public class ServiceAop implements Ordered { @Pointcut("this(com.example.junitspringboot.service.ISwimming)") public void thisPointcut(){} @Before("thisPointcut()") public void before(JoinPoint joinPoint) { System.out.println("before"); } @Override public int getOrder() { return 1; } } // 使用引介使 Man 也实现 ISwimming 接口 @Component @Aspect public class IntroductionAop implements Ordered { @DeclareParents(value = "com.example.junitspringboot.service.Man", defaultImpl = Swimming.class) public ISwimming swimming; @Override public int getOrder() { return 0; } } // 微信公众号:CoderLi public interface ISwimming { void swim(); } // 微信公众号:CoderLi @Service public class Man implements Person{ @Override public void say() { System.out.println("man"); } } ConfigurableApplicationContext context = SpringApplication.run(JunitSpringBootApplication.class, args); context.getBean(Man.class).say(); ((ISwimming) context.getBean(Man.class)).swim(); 复制代码
当我们调用 swim 方法的时候、会被拦截增强。当我们调用 say 方法的时候、同样也会被拦截增强,这个就是 this 会将代理类实现的其他接口的方法也会被拦截增强
target
target 通过判断目标类是否按类型匹配指定类来决定连接点是否匹配. 用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
@Pointcut("target(com.example.junitspringboot.service.ISwimming)") 复制代码
同样也是上面的例子。修改切点表达式为 target 、say 方法不再被拦截增强。
args
用来匹配参数
@Pointcut("args(..)") 复制代码
- 匹配任意参数的方法
@Pointcut("args()") 复制代码
- 匹配任何不带参数的方法
@target
匹配当被代理的目标对象对应的类型及其父类型上拥有指定的注解时
@Pointcut("@target(com.example.junitspringboot.anno.AopFlag) && within(com.example.junitspringboot..*)") 复制代码
- 匹配在包 com.example.junitspringboot..* 下所有被 AopFlag 修饰的类的所有方法
@args
@args匹配被调用的方法上含有参数,且对应的参数类型上拥有指定的注解的情况。
@Pointcut("@args(com.example.junitspringboot.anno.AopFlag)") // 微信公众号:CoderLi @AopFlag public class Data { } public interface Person { void say(Data data); } 复制代码
@within
@within用于匹配被代理的目标对象对应的类型或其父类型拥有指定的注解的情况,但只有在调用拥有指定注解的类上的方法时才匹配。
@Pointcut("@within(com.example.junitspringboot.anno.AopFlag) && within(com.example.junitspringboot..*)") 复制代码
这个功能上貌似跟 @target 有点像了
@annotation
也是比较常用的一个注解、用于匹配方法上拥有指定注解的情况。
@Pointcut("@annotation(com.example.junitspringboot.anno.AopFlag) && within(com.example.junitspringboot..*)") 复制代码
bean
Spring 特有的一个表达式。
@Pointcut("bean(man)") 复制代码
拦截该 bean 的所有方法
10 种切点的表达式介绍完毕