关于Spring AOP中切点修饰符@annotation、@args与args约束说明
于其说这是一篇文章,不如说这是一篇笔记,主要介绍了@annotation、@args和args的作用以及一些坑点。
前言
于其说这是一篇文章,不如说这是一篇笔记,主要介绍了@annotation
、@args
和args
的作用以及一些坑点。这里主要记录一些项目用到的,没有成一套体系,网上其他文章对Spring AOP的切点修饰符可能有比较全的描述。如果以后有遇到其他场景,会在这里补充。
背景
主要是实习公司需要开发一个注解来实现某些特定功能,项目是基于Spring Boot搭建的,因此很容易想到Spring的AOP技术来实现。通过查阅资料和官方文档,发现@annotation
注解可以满足这个需求。同时我也研究了@args
与·args
两个修饰符,讨论一下这两个修饰符到底在干什么。
Spring AOP原理
Spring的AOP使用动态代理模式。在代理模式中有两个对象:代理对象与被代理对象(目标对象),代理模式中是说使用代理对象来操作被代理对象,而动态代理,说明这个代理对象可以根据需要生成,这就是“动”的含义。
关键就在于这个代理对象,我们既然可以在代理对象中调用被代理对象的方法,那么我们就可以在方法执行前、后等做一些操作,这种操作叫做类增强。我们定义方法,写各种各样的表达式,目标就是要匹配正确的方法做类增强。
所以,我们就遇到了第一个坑点:为什么AOP的切点定义不起作用?
大方向有两个原因:
- 切点定义有问题
- Spring为你生成的根本不是代理对象
第二点的排查比较容易,找到你期望增强的方法,然后运行debug模式,观察定义目标方法的对象是不是代理对象:
例如,TestController
就不是代理对象,xServiceImpl
就是代理对象,而且是通过GCLIB进行动态代理的
相关阅读:spring 依赖注入时,什么时候会创建代理类
切点修饰符
@annotation
定义
官方定义:
Limits matching to join points where the subject of the join point (the method being run in Spring AOP) has the given annotation.
也就是说,如果把你定义的注解修饰在某一个方法上,那么就会命中,执行定义的类增强逻辑。
例子
定义一个注解@append
,用于在字符串前后增加一些符号。例如接收到的字符串为hello
,输出*** hello ***
。
- Append注解定义
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Append {
public String word() default "***";
}
- 注解处理器定义
/**
* Append的注解处理器
*/
@Aspect
@Component
public class AppendProcessor {
@Around("@annotation(appendAnnotation)")
public String process(ProceedingJoinPoint joinPoint, Append appendAnnotation) throws Throwable{
String res = appendAnnotation.word() + " " + joinPoint.proceed() + " " + appendAnnotation.word();
return res;
}
}
@annotation(…)传入的是注解对象的引用,可以是类型引用
- service方法定义
@Service
public class XServiceImpl {
@Append
public String foo(String val) {
return val;
}
}
- controller定义
@Controller
@RestController
public class TestController {
@Autowired
private XServiceImpl xService;
@GetMapping("/hello")
public String hello() {
return xService.foo("Hello Word!");
}
}
运行结果(关注控制台):
args族
对于args约束,我的理解是用于限制匹配方法的参数类型。这个参数有两种,一种是针对普通方法的,另外一种是针对注解的
args
官方定义
Limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types.
这是针对普通方法的,从定义中可以知道,args
用来限制匹配方法的参数类型。
使用args,可以传递匹配方法的调用参数
例子
与@annotation
那一节中的例子一样,我们修改一下注解处理器
- 注解处理器定义
/**
* Append的注解处理器
*/
@Aspect
@Component
public class AppendProcessor {
@Around("@annotation(appendAnnotation) && args(val)")
public String process(ProceedingJoinPoint joinPoint, Append appendAnnotation, String val) throws Throwable{
System.out.println(val);
String res = appendAnnotation.word() + " " + joinPoint.proceed() + " " + appendAnnotation.word();
return res;
}
}
运行结果:
同时观察控制台,会发现val的值是调用匹配方法所传递的值,即Hello Word!
如果我把注解处理器中val的类型改成Integer会发生什么呢?
/**
* Append的注解处理器
*/
@Aspect
@Component
public class AppendProcessor {
@Around("@annotation(appendAnnotation) && args(val)")
public String process(ProceedingJoinPoint joinPoint, Append appendAnnotation, Integer val) throws Throwable{
System.out.println(val);
String res = appendAnnotation.word() + " " + joinPoint.proceed() + " " + appendAnnotation.word();
return res;
}
}
运行结果:
可以发现,方法匹配失败了,因为打了@Append
注解方法的第一个参数是String,而不是Integer
@args
官方定义
Limits matching to join points (the execution of methods when using Spring AOP) where the runtime type of the actual arguments passed have annotations of the given types.
有@
标志的说明跟注解有关,用来描述方法参数的封装类是否有某个注解。这某个注解的作用域要有类
例子
在@annotation
那一节中的例子的基础上,定义一个新的注解@Demo
@Demo
注解定义
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Demo {
}
- 新定义一个
YService
,与XService
做区分,YService
上有@Demo
注解
@Service
@Demo
public class YServiceImpl {
public void foo() {}
}
- 修改
Xservice
中的foo
方法:
@Service
public class XServiceImpl {
@Append
public String foo(YServiceImpl yService, String val) {
System.out.println("方法开始执行");
return val;
}
}
- 在注解处理器中新增一个方法
@Around("@annotation(appendAnnotation) && @args(cn.acmsmu.aop.Demo,..)")
public String process2(ProceedingJoinPoint joinPoint, Append appendAnnotation) throws Throwable{
String res = appendAnnotation.word() + " " + joinPoint.proceed() + " " + appendAnnotation.word();
System.out.println(res);
return res;
}
这里的
@args
表示匹配方法可以有多个参数,第一个参数的类必须被@Demo
注解修饰
运行结果
共同点与区别
- 共同点:都是对目标方法的参数类型进行限制
- 区别:
args
: 单纯针对待增强方法的参数类型,不会关系参数的类@args
: 关注待增强方法参数的类是否被某个注解修饰
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)