先小结一下

  1. args()是用来匹配并且接收目标方法的参数的.<br /> argNames(用在注解中)与arg-names(用在XML中),他们是同一个东西.<br /> argNames用来接收AspectJ表达式中的参数(其中也包含args).<br /> 可以通过调整argNames参数名的顺序,来调整参数传值的顺序.

schema

Spring AOP中args()和argNames的含义 - 图1

aspectJ

Spring AOP中args()和argNames的含义 - 图2

下面是一些补充:

1.Spring本身对AOP的支持并不是很好,所以后来引入了AspectJ.
2.Spring支持两种风格,Schema(XML配置), 以及AspectJ(注解配置).(参考地址)
3.Schema风格XML配置时切入点语法默认是AspectJ语法.
4.args说明: AspectJ语法,通常出现在定义切入点时
XML配置时

  1. -- 注意是使用and链接
  2. <aop:pointcut expression="execution(* *.*(..)) and arg(paramName)"/>
  3. <aop:(before|after|..) pointcut="execution(* *.*(..)) and args(paramName)", arg-names="paramName"/>
  1. 注解配置时
  1. // 如果在@Pointcut中定义了args,那么也要定义argNames,并且两者的参数名称必须一样,顺序可以不一样,(有其他作用)
  2. // 注意是使用&&链接
  3. @Pointcut(value="execution(* *.*(..)) && args(paramName)", argNames="paramName")

5.arg-names说明:
XML配置时

  1. <aop:(before|after|..) ... arg-names="paramName"/> // 通知方法的定义中

注解配置时

  1. @Pointcut(...,argNames="paramName") // 切入点的定义中
  2. @Before(..., argNames="paramName") // 通知方法的定义中

6.args可以用来定义目标方法必须要具备的参数个数,而且执行通知的方法也必须带相同个数参数|或者不带参数. 提示:通知方法中的参数类型可以是目标参数的类型或者目标参数的父类型.(推理一下如果通知方法与目标方法类型不一致会直接导致ClassCastException异常)

7.args与args-names是引用的关系,并且是根据参数名称来传递值(不是顺序),所以参数名字必须一致
— 我猜是args的参数值传递给argNames,

8.基于注解配置时更特殊,除上面两种约束之外
在@pointcut中args与args-names必须成对出现

9.AspectJ语法(参考地址)

args()的作用主要有两点

1、切入点表达式部分如果增加了args()部分,那么目标方法除了要满足execution部分,还要满足args()对方法参数的要求,对于符合execution表达式,但不符合args参数的方法,不会被植入切面。

2、定义了args()之后,才能把目标方法的参数传入到切面方法的参数中(通过Joinpoint也可以获取参数,但当前方法是直接用切面方法参数接受)。

示例1

目标方法:

  1. @RestController
  2. @RequestMapping("/testAop")
  3. public class TestController {
  4. private Logger logger = LoggerFactory.getLogger(TestController.class);
  5. @RequestMapping("/helloworld")
  6. public String helloWorld(String id, Integer age){
  7. System.out.println("被代理方法正在执行");
  8. return null;
  9. }
  10. }

切面方法

  1. @After("execution(* com.bxp.controller.TestController.*(..)) && args(userId, userAge)")
  2. public void after(JoinPoint point, String userId, Integer userAge){
  3. System.out.println("userId===========" + userId);
  4. System.out.println("userAge===========" + userAge);
  5. }

输出结果:

  1. 被代理方法正在执行
  2. userId===========bian1996
  3. userAge===========24

定义了args(userId, userAge)才能把目标方法helloWorld(String id, Integer age)的参数传入到增强处理方法after的参数中,id参数对应userId,age参数对应userAge。使用的方法是按顺序一一对应,helloWorld第一个参数对args第一个参数,helloWorld第2个参数对args第2个参数。

切入点表达式部分增加了&&args(userId, userAge)部分,意味着可以在增强处理方法中定义userId、userAge两个形参———定义这两个形参时,形参类型可以随意指定,但是一旦指定,譬如这里分别是String类型和Integer类型,这两个形参类型将用于限制该切入点只匹配第一个参数类型为String,第二个参数类型为Integer的方法。

也就是,args()中的参数会和目标方法的参数除了在顺序上一一对应之外,在类型上也要对应,否则匹配失败,如下两种情况都会匹配失败。

  1. @RequestMapping("/helloworld")
  2. public String helloWorld(Integer id, Integer age){
  3. System.out.println("被代理方法正在执行");
  4. return null;
  5. }
  6. @After("execution(* com.bxp.controller.TestController.*(..)) && args(userId, userAge)")
  7. public void after(JoinPoint point, String userId, String userAge){
  8. System.out.println("userId===========" + userId);
  9. System.out.println("userAge===========" + userAge);
  10. }
  11. @RequestMapping("/helloworld")
  12. public String helloWorld(Integer sex, String id, Integer age){
  13. System.out.println("被代理方法正在执行");
  14. return null;
  15. }
  16. @After("execution(* com.bxp.controller.TestController.*(..)) && args(userId, userAge)")
  17. public void after(JoinPoint point, String userId, Integer userAge){
  18. System.out.println("userId===========" + userId);
  19. System.out.println("userAge===========" + userAge);
  20. }

除此之外,使用args()表达式时还可使用如下形式:args(userId, userAge,..),这表明增强处理方法中可以通过userId, userAge来访问目标方法的参数。注意上面args表达式括号中的2点,它表示可以匹配更多参数,但是只要前两个userId, userAge参数匹配上了,目标方法就可以被匹配上。

argNames是可选的,如果没有argNames这个参数,而编译器设置了【在class文件生成变量调试信息】,则spring可以通过反射知道方法参数的名字,通过名字配对,Spring知道args(userId, userAge)表达式里面的userId和userAge,对应了增强方法public void after(JoinPoint point, String userId, Integer userAge)方法里面的userId和userAge,就是第一个示例的情况:

总结:
目标方法和args()通过参数顺序一一进行匹配
args()和增强方法通过参数名称一致进行匹配。

但是,如果设置了argNames,Spring不再使用方法参数的名字来配对,使用argNames定义的顺序来给

after(JoinPoint point, String userAge, String userId)的参数传值,例如:argNames=”userId,userAge”,userId在userAge前面,表示after方法第一个参数(JoinPoint 除外)是userId,第二个参数是userAge,示例如下:

目标方法

  1. @RequestMapping("/helloworld")
  2. public String helloWorld(String id, String age){
  3. System.out.println("被代理方法正在执行");
  4. return null;
  5. }

切面方法

  1. @After(value = "execution(* com.bxp.controller.TestController.*(..)) && args(userId, userAge)", argNames = "userId,userAge")
  2. public void after(JoinPoint point, String userAge, String userId){
  3. System.out.println("userId===========" + userId);
  4. System.out.println("userAge===========" + userAge);
  5. }

请求连接和输出结果

  1. 请求连接
  2. http://localhost:8088/testAop/helloworld?age=24&id=bian1996
  3. 输出结果
  4. 被代理方法正在执行
  5. userId===========24
  6. userAge===========bian1996
  7. 注意:这一次两个参数的类型都给成String类型了

总结:
目标方法和args()通过参数顺序一一进行匹配
args()和argNames通过参数名称一致进行匹配
argNames和增强方法通过参数顺序一一对应。