关于Spring AOP中切点修饰符@annotation、@args与args约束说明

2022-06-27 09:17:47

前言

于其说这是一篇文章,不如说这是一篇笔记,主要介绍了@annotation@argsargs的作用以及一些坑点。这里主要记录一些项目用到的,没有成一套体系,网上其他文章对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 (themethod being run in Spring AOP) has the given annotation.

也就是说,如果把你定义的注解修饰在某一个方法上,那么就会命中,执行定义的类增强逻辑。

例子

定义一个注解@append,用于在字符串前后增加一些符号。例如接收到的字符串为hello,输出*** hello ***

  • Append注解定义
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceAppend{publicStringword()default"***";}
  • 注解处理器定义
/**
 * Append的注解处理器
 */@Aspect@ComponentpublicclassAppendProcessor{@Around("@annotation(appendAnnotation)")publicStringprocess(ProceedingJoinPoint joinPoint,Append appendAnnotation)throwsThrowable{String res= appendAnnotation.word()+" "+ joinPoint.proceed()+" "+ appendAnnotation.word();return res;}}

@annotation(…)传入的是注解对象的引用,可以是类型引用

  • service方法定义
@ServicepublicclassXServiceImpl{@AppendpublicStringfoo(String val){return val;}}
  • controller定义
@Controller@RestControllerpublicclassTestController{@AutowiredprivateXServiceImpl xService;@GetMapping("/hello")publicStringhello(){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@ComponentpublicclassAppendProcessor{@Around("@annotation(appendAnnotation) && args(val)")publicStringprocess(ProceedingJoinPoint joinPoint,Append appendAnnotation,String val)throwsThrowable{System.out.println(val);String res= appendAnnotation.word()+" "+ joinPoint.proceed()+" "+ appendAnnotation.word();return res;}}

运行结果:

同时观察控制台,会发现val的值是调用匹配方法所传递的值,即Hello Word!

如果我把注解处理器中val的类型改成Integer会发生什么呢?

/**
 * Append的注解处理器
 */@Aspect@ComponentpublicclassAppendProcessor{@Around("@annotation(appendAnnotation) && args(val)")publicStringprocess(ProceedingJoinPoint joinPoint,Append appendAnnotation,Integer val)throwsThrowable{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@interfaceDemo{}
  • 新定义一个YService,与XService做区分,YService上有@Demo注解
@Service@DemopublicclassYServiceImpl{publicvoidfoo(){}}
  • 修改Xservice中的foo方法:
@ServicepublicclassXServiceImpl{@AppendpublicStringfoo(YServiceImpl yService,String val){System.out.println("方法开始执行");return val;}}
  • 在注解处理器中新增一个方法
@Around("@annotation(appendAnnotation) && @args(cn.acmsmu.aop.Demo,..)")publicStringprocess2(ProceedingJoinPoint joinPoint,Append appendAnnotation)throwsThrowable{String res= appendAnnotation.word()+" "+ joinPoint.proceed()+" "+ appendAnnotation.word();System.out.println(res);return res;}

这里的@args表示匹配方法可以有多个参数,第一个参数的类必须被@Demo注解修饰

运行结果

共同点与区别

  • 共同点:都是对目标方法的参数类型进行限制
  • 区别:
    • args: 单纯针对待增强方法的参数类型,不会关系参数的类
    • @args: 关注待增强方法参数的类是否被某个注解修饰
  • 作者:mgsky1
  • 原文链接:https://blog.csdn.net/mgsky1/article/details/123156911
    更新时间:2022-06-27 09:17:47