AspectJ
在实现 AOP 过程中提供了丰富的切点表达式用于表述满足条件的目标方法,如execution
、within
、this
、target
、args
等表达式,它们各自通过自身的特征来匹配符合条件的目标方法。
我们这里讲一下 execution()
与 args()
表达式的区别。
execution()
与 args()
表达式的区别
args
表达式可以限制连接点匹配参数为指定类型的执行方法,而 execution
表达式同样可以实现限制连接点匹配参数的指定类型,那么这两个表达式具体匹配的效果有什么区别呢?
如下有两个表达式:
args(java.io.Serializable)
#--------------------
execution(* *(java.io.Serializable))
第一个args
表达式表示对于任意方法,匹配方法只有一个入参且该入参实现了Serializable
接口,第二个表达式表示的含义类似。
args(java.io.Serializable)
表达式匹配在执行方法时 传入目标方法的参数对象是否实现了Serializable
接口。对于任意方法,执行方法时传入参数只有一个且该参数的类型实现Serializable
接口则满足表达式。
execution(* *(java.io.Serializable))
表达式则匹配方法定义的形参是否为Serializable
。对于任意方法,方法定义时指定的形参只有一个且该参数类型为 Serializable
则满足表达式。
args
情形
execution
情形
换言之,args
表达式专注于那些在运行期间执行方法时实际传入的参数类是否实现了Serializable
,而execution
则专注于方法的定义里面形参类型是否为Serializable
。
示例
下面编写一段代码来验证上面的逻辑:
@Data
public class UserDTO implements Serializable{
private String name;
}
@Service
public class DemoService{
// 符合 args(java.io.Serializable) 表达式,但不限定与该方法
public void method_1(Object obj){
log.info("method_1's param is {}",obj)
}
// 符合 execution(* *(java.io.Serializable)) 表达式
public void method_2(Serializable user){
log.info("method_2's param is {}",user);
}
// 不符合 execution(* *(java.io.Serializable)) 表达式
// 因为 execution 要求参数类型完全匹配,而不是继承或者实现关系
public void method_3(UserDTO user){
log.info("method_3's param is {}",user);
}
}
@Aspect
@Component
public class DemoAspect{
@Pointcut("execution(* com.example..*.*(..))")
public void cut1(){}
@Before("execution(* com.example..*.*(java.io.Serializable))")
public void execution(JoinPoint joinPoint) throws Throwable {
log.info("{} has executed by execution ---",joinPoint.getSignature().getName());
}
@Before("cut1() && args(java.io.Serializable)")
public void args(JoinPoint joinPoint) throws Throwable {
log.info("{} has executed by args ===",joinPoint.getSignature().getName());
}
}
@RestController
public class DemoController{
@Autowired
DemoService demoService;
@GetMapping("test1")
public void test1(){
final UserDTO user = new UserDTO();
user.setName("ghimi");
demoService.method_1(user);
}
@GetMapping("test2")
public void test2(){
final UserDTO user = new UserDTO();
user.setName("ghimi");
demoService.method_2(user);
}
}
对于上述示例,当我请求 test1()
时,切面 method_1
被匹配并调用,当我请求 test2()
时,切面method_2
被匹配并调用。由此可见args
匹配的是运行时期传入的参数,而 execution
匹配的是方法定义的参数。
当两者同时使用时,如果传入参数同时满足execution
与 args
表达式时,则会优先匹配 args
条件,然后匹配execution
条件。
如上面的示例,当我请求 demoService.method_2()
时,方法既满足 execution(* *(java.io.Serializable))
表达式,又同时满足 args(java.io.Serializable)
表达式,此时会先匹配 args
表达式,然后匹配 execution
表达式。
参考资料
Spring AOP 切点指示符中execution和args的区别
AOP with Spring
Introduction of Spring AOP
Join Point Matching based on Annotations
spring学习笔记-切点表达式函数详解