args函数的入参是类名, 而 @args()的入参必须是注解类的类名。
虽然args()允许在类名后使用“+”通配符,但该通配符在此处没有意义,添加和不添加的效果都一样。
该函数接收一个类名,表示目标类方法入参对象是指定类(包含子类)时,切点匹配。
比如args(com.xgj.Waiter) 表示运行时入参是Waiter类型的方法,它和execution(* *(com.xgj.Waiter))的区别在于后者是这对类方法的签名而言的,而前者是针对运行时的入参类型而言。
比如args(com.xgj.Waiter)既匹配addWiter(Waiter waiter),又匹配addNaiveWaiter(NaiveWaiter waiter); 而 execution(* *(com.xgj.Waiter)),实际上 args(com.xgj.Waiter)等价于 execution(* *(com.xgj.Waiter+)),当然也等价于 args(com.xgj.Waiter+)
该函数接收一个注解类的类名,当方法的运行时入参对象标注了指定的注解时,匹配切点。
我们来通过下图@args(M)匹配示意图来详细解释下:
T0、T1、T2、T3有如上继承关系,假设目标类方法的签名为fun(T1 t),它的入参为T1,而切面的切点定义为@args(M), T2类标注了@M。 当fun(T1 t)的传入对象为T2或者T3时,方法匹配@args(M)声明所定义的切点。
假设方法签名是funt(T1 t),入参为T1,而标注了@M的类是T0,当fun(T1 t)传入T1、T2、T3的实例时,均不匹配切点@args(M).
在类的继承树中,(1)处为方法签名中入参类型在继承树的位置,称之为入参类型点, 而(2)处标注了@M注解的类在类继承树中的位置,称之为注解点。 判断方法在运行时是否匹配@args(M)切点,可以根据(1)和(2)在类继承树中的相对位置来判断。
如果在继承树中注解点(2)高于入参类型点(1),则该目标方法不可能匹配到切点@args(M) ,如下图所示 如果在类继承树中注解点(2)低于入参类型点(1),则注解点所在类及其子孙类作为方法入参时,该方法匹配切点@args(M)实例请参考下文。
代码已托管到Github—> https://github.com/yangshangwei/SpringMaster
POJO
package com.xgj.aop.spring.advisor.aspectJ.function.args; import org.springframework.stereotype.Component; @Component public class UserService { public void addUser(User user) { System.out.println("addUser " + user); } public void modifyUser(User user) { System.out.println("modifyUser " + user); } public void delUser(User user) { System.out.println("delUser " + user); } /** * * * @Title: addArtisanTT * * @Description: 入参为ArtisanTT * * @param artisan * * @return: void */ public void addArtisanTT(ArtisanTT artisan) { System.out.println("addArtisanTT " + artisan); } public void modifyArtisanTT(ArtisanTT artisan) { System.out.println("modifyArtisanTT " + artisan); } public void delArtisanTT(ArtisanTT artisan) { System.out.println("delArtisanTT " + artisan); } }POJO
package com.xgj.aop.spring.advisor.aspectJ.function.args; import org.springframework.stereotype.Component; @Component public class UserServiceExt { public void addUser(User user) { System.out.println("入参为user的类 addUser " + user); } public void modifyUser(User user) { System.out.println("入参为user的类 modifyUser " + user); } public void delUser(User user) { System.out.println("入参为user的类 delUser " + user); } /** * * * @Title: addArtisanTT * * @Description: 入参为ArtisanTT * * @param artisan * * @return: void */ public void addArtisanTT(ArtisanTT artisan) { System.out.println("addArtisanTT " + artisan); } public void modifyArtisanTT(ArtisanTT artisan) { System.out.println("modifyArtisanTT " + artisan); } public void delArtisanTT(ArtisanTT artisan) { System.out.println("delArtisanTT " + artisan); } }切面
package com.xgj.aop.spring.advisor.aspectJ.function.args; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; /** * * * @ClassName: ArgsAspectJ * * @Description: 该函数接收一个类名,表示目标类方法入参对象是指定类(包含子类)时,切点匹配 * * @author: Mr.Yang * * @date: 2017年9月1日 上午11:36:23 */ @Aspect public class ArgsAspect { @Before("args(com.xgj.aop.spring.advisor.aspectJ.function.args.User)") public void crossCuttingCode() { System.out.println("some logic is here "); } }配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- (1)声明Context命名空间以及Schema文件 (2)扫描类包以及应用注解定义的bean --> <context:component-scan base-package="com.xgj.aop.spring.advisor.aspectJ.function.args"/> <!-- 基于@AspectJ切面的驱动器 --> <aop:aspectj-autoproxy proxy-target-class="true"/> <!-- 使用了@AspectJ注解的切面类 --> <bean class="com.xgj.aop.spring.advisor.aspectJ.function.args.ArgsAspect"/> </beans>测试类:
package com.xgj.aop.spring.advisor.aspectJ.function.args; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class ArgsAspectTest { private ApplicationContext ctx; @Test public void test() { ctx = new ClassPathXmlApplicationContext("classpath:com/xgj/aop/spring/advisor/aspectJ/function/args/conf-args.xml"); UserService userService = ctx.getBean("userService", UserService.class); UserServiceExt userServiceExt = ctx.getBean("userServiceExt", UserServiceExt.class); User user = new User(); ArtisanTT artisan = new ArtisanTT(); // 织入横切逻辑 userService.addUser(user); // 织入横切逻辑 userService.modifyUser(user); // 织入横切逻辑 userService.delUser(user); System.out.println("================================"); // 入参不是user,因此不会被织入横切逻辑 userService.addArtisanTT(artisan); userService.modifyArtisanTT(artisan); userService.delArtisanTT(artisan); System.out.println("================================"); // 入参为user,因此也会被织入横切逻辑 userServiceExt.addUser(user); // 入参为user,因此也会被织入横切逻辑 userServiceExt.modifyUser(user); // 入参为user,因此也会被织入横切逻辑 userServiceExt.delUser(user); System.out.println("================================"); // 入参不是user,因此不会被织入横切逻辑 userServiceExt.addArtisanTT(artisan); userServiceExt.modifyArtisanTT(artisan); userServiceExt.delArtisanTT(artisan); } }运行结果:
2018-01-04 09:19:50,614 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4a05bc0c: startup date [Thu Jan 04 09:19:50 CST 2018]; root of context hierarchy 2018-01-04 09:19:50,795 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/aspectJ/function/args/conf-args.xml] some logic is here addUser com.xgj.aop.spring.advisor.aspectJ.function.args.User@657531ef some logic is here modifyUser com.xgj.aop.spring.advisor.aspectJ.function.args.User@657531ef some logic is here delUser com.xgj.aop.spring.advisor.aspectJ.function.args.User@657531ef ================================ addArtisanTT com.xgj.aop.spring.advisor.aspectJ.function.args.ArtisanTT@712175f2 modifyArtisanTT com.xgj.aop.spring.advisor.aspectJ.function.args.ArtisanTT@712175f2 delArtisanTT com.xgj.aop.spring.advisor.aspectJ.function.args.ArtisanTT@712175f2 ================================ some logic is here 入参为user的类 addUser com.xgj.aop.spring.advisor.aspectJ.function.args.User@657531ef some logic is here 入参为user的类 modifyUser com.xgj.aop.spring.advisor.aspectJ.function.args.User@657531ef some logic is here 入参为user的类 delUser com.xgj.aop.spring.advisor.aspectJ.function.args.User@657531ef ================================ addArtisanTT com.xgj.aop.spring.advisor.aspectJ.function.args.ArtisanTT@712175f2 modifyArtisanTT com.xgj.aop.spring.advisor.aspectJ.function.args.ArtisanTT@712175f2 delArtisanTT com.xgj.aop.spring.advisor.aspectJ.function.args.ArtisanTT@712175f2首先我们先自定义一个注解,用于测试用
package com.xgj.aop.spring.advisor.aspectJ.function.args.atargs; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * * * @ClassName: Monitor * * @Description: 自定义注解 @Monitor * 更多信息请阅读http://blog.csdn.net/yangshangwei/article/ * details/77477840 * * @author: Mr.Yang * * @date: 2017年9月1日 下午4:00:12 */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented public @interface Monitor { public boolean value() default true; }使用注解扫描的4个POJO
package com.xgj.aop.spring.advisor.aspectJ.function.args.atargs; import org.springframework.stereotype.Component; @Component public class T0 { } package com.xgj.aop.spring.advisor.aspectJ.function.args.atargs; import org.springframework.stereotype.Component; @Component public class T1 extends T0 { /** * * * @Title: fun * * @Description: 目标类方法,旨在这个方法中织入增强逻辑. 当注解标注在T2,方法的入参为T2或者T2的子孙类时,会织入增强 * * @param t * * @return: void */ public void fun(T2 t) { System.out.println(t.getClass().getName() + " fun executed"); } } package com.xgj.aop.spring.advisor.aspectJ.function.args.atargs; import org.springframework.stereotype.Component; @Monitor @Component public class T2 extends T1 { } package com.xgj.aop.spring.advisor.aspectJ.function.args.atargs; import org.springframework.stereotype.Component; @Component public class T3 extends T2 { }切面
package com.xgj.aop.spring.advisor.aspectJ.function.args.atargs; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; /** * * * @ClassName: AtArgsAspect * * @Description: 标注了@Aspect的切面 * * @author: Mr.Yang * * @date: 2017年9月1日 下午4:21:14 */ @Aspect public class AtArgsAspect { @Before("@args(com.xgj.aop.spring.advisor.aspectJ.function.args.atargs.Monitor)") public void crossCuttingCode() { System.out.println("前置增强 横切逻辑织入 some logic is here "); } }配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- (1)声明Context命名空间以及Schema文件 (2)扫描类包以及应用注解定义的bean --> <context:component-scan base-package="com.xgj.aop.spring.advisor.aspectJ.function.args.atargs"/> <!-- 基于@AspectJ切面的驱动器 --> <aop:aspectj-autoproxy proxy-target-class="true"/> <!-- 使用了@AspectJ注解的切面类 --> <bean class="com.xgj.aop.spring.advisor.aspectJ.function.args.atargs.AtArgsAspect"/> </beans>测试类
package com.xgj.aop.spring.advisor.aspectJ.function.args.atargs; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AtArgsAspectTest { @Test public void test() { ApplicationContext ctx = new ClassPathXmlApplicationContext( "classpath:com/xgj/aop/spring/advisor/aspectJ/function/args/atargs/conf-atargs.xml"); T0 t0 = ctx.getBean("t0", T0.class); T1 t1 = ctx.getBean("t1", T1.class); T2 t2 = ctx.getBean("t2", T2.class); T3 t3 = ctx.getBean("t3", T3.class); // 因t1中的fun入参为t2,且注解标注在了T2类上,t3又是t2的子类,所以 下面两个调用都会织入增强 t1.fun(t2); t1.fun(t3); } }运行结果:
2017-09-04 17:43:44,302 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@f4efcb0: startup date [Mon Sep 04 17:43:44 BOT 2017]; root of context hierarchy 2017-09-04 17:43:44,390 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/aspectJ/function/args/atargs/conf-atargs.xml] 前置增强 横切逻辑织入 some logic is here com.xgj.aop.spring.advisor.aspectJ.function.args.atargs.T2$$EnhancerBySpringCGLIB$$fcff1873 fun executed 前置增强 横切逻辑织入 some logic is here com.xgj.aop.spring.advisor.aspectJ.function.args.atargs.T3$$EnhancerBySpringCGLIB$$109aa116 fun executed从运行结果看,正确的织入了横切逻辑。
在类继承树中注解点低于入参类型点,则注解点所在类及其子孙类作为方法入参时,该方法匹配切点@args(M), 符合。
如果我们先取消掉T2上的@Monitor注解,然后把注解标注在T0上
如下:
package com.xgj.aop.spring.advisor.aspectJ.function.args.atargs; import org.springframework.stereotype.Component; @Monitor @Component public class T0 { }运行结果:
2017-09-04 17:56:31,858 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1c057a1e: startup date [Mon Sep 04 17:56:31 BOT 2017]; root of context hierarchy 2017-09-04 17:56:31,970 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/aspectJ/function/args/atargs/conf-atargs.xml] com.xgj.aop.spring.advisor.aspectJ.function.args.atargs.T2$$EnhancerBySpringCGLIB$$42855f4c fun executed com.xgj.aop.spring.advisor.aspectJ.function.args.atargs.T3$$EnhancerBySpringCGLIB$$5620e7ef fun executed因在继承树中注解点高于入参类型点,因此该目标方法不可能匹配到切点@args(M)。