最近在看Spring AOP整合AspectJ的源码时,因为Pointcut的expression中的args配置不太熟悉,而args是除了execution外最经常使用的配置,于是搜索网上以及官方的文档,加上实例测试作一些总结。html
在Spring的官方文档,对args的定义以下java
args - limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given typesspring
说明args中指定的是方法的入参类型, 而Spring AOP会匹配当前执行的方法的入参类型是否为args中给定的类型。sql
根据Spring文档的指导,咱们来看下XML中args的配置。express
<aop:pointcut expression="execution(* com.lcifn.spring.aop.bean.*.*(..)) and args(String,java.util.Date,..)"/>
这个pointcut匹配的就是com.lcifn.spring.aop.bean包下任意类,且方法的第一个参数为String类型,第二个参数为java.util.Date类型。对于java.lang包下的类直接指定类名便可,而其余的则须要指定包名,而..表示方法能够不止两个参数。测试
这个pointcut能匹配的方法以下.net
public String seeMovie(String movie, Date date, String personName){}
若是须要在Advice的方法中引入当前执行方法的参数,咱们就须要使用arg-names,它是配置于advice级别的属性。咱们延续上面的例子,配置一个环绕加强,而且引入前两个参数。code
public Object aroundIntercept(ProceedingJoinPoint pjp, String music, Date date){}
此时的xml配置依照猜想来看会这样配置component
<aop:config proxy-target-class="true"> <aop:pointcut id="pointcut" expression="execution(* com.lcifn.spring.aop.bean.*.*(..)) and args(String,java.util.Date,..)"/> <aop:aspect ref="advice"> <aop:around method="aroundIntercept" pointcut-ref="pointcut" arg-names="String,java.util.Date"/> </aop:aspect> </aop:config>
可是运行测试后提示arg-names中的java.util.Date非法,也就是不能使用点分隔符。来看正确的配置xml
<aop:config proxy-target-class="true"> <aop:pointcut id="pointcut" expression="execution(* com.lcifn.spring.aop.bean.*.*(..)) and args(str,date,..)"/> <aop:aspect ref="advice"> <aop:around method="aroundIntercept" pointcut-ref="pointcut" arg-names="str,date"/> </aop:aspect> </aop:config>
arg-names中的变量必须同args中的一致,这是确定的,那会有人问,args中原来配置的是参数的类型,如今没有了类型,还有用吗?其实此时的类型匹配已经转移到advice类中的方法的参数上了。能够看到上面的aroundIntercept方法中,后两个参数不是都有类型吗,Spring经过此种方式实现了advice在运行时接收原始方法的参数。很麻烦是否是,并且特别容易出错,我作测试的时候都要反反复复改几遍。XML很差用,咱们来看看注解的方式。
仍是以环绕加强举例,经过@Aspect标注切面类
@Component [@Aspect](https://my.oschina.net/aspect) public class AspectJAnnotationBrowserAroundAdvice { @Pointcut("execution(* com.lcifn.spring.aop.bean.ChromeBrowser.*(..))") private void pointcut(){ } @Around(value="pointcut()") public Object aroundIntercept(ProceedingJoinPoint pjp) throws Throwable{ Object retVal = pjp.proceed(); return retVal; } }
注解方式的AOP通常都会使用component-scan方式,于是要在切面类上加上@Component注解。@Pointcut通常放在一个空方法上,其值为切入点表达式;@Around标识环绕加强,value值为切入点的方法名,或者直接写切入点表达式也能够。
咱们须要使用args配置,从而在加强方法中调用方法运行时的参数值,来看下其配置
@Component @Aspect public class AspectJAnnotationArgsBrowserAroundAdvice { @Pointcut("execution(* com.lcifn.spring.aop.bean.ChromeBrowser.*(..)) && args(music,date,..)") private void pointcut(String music, Date date){ } @Around(value="pointcut(music,date)") public Object aroundIntercept(ProceedingJoinPoint pjp, String music, Date date) throws Throwable{ Object retVal = pjp.proceed(); return retVal; } }
args中使用昵称方式,且必须同切入点方法pointcut中的参数名称一致(为何能匹配到参数名称将在下一章重点介绍)。目标方法参数的类型则由pointcut的参数类型及顺序来决定,这同xml配置中一致。
而加强方法引用切入点方法时,则必须将切入点方法中的参数带上,如上述代码中的@Around(value="pointcut(music,date)")
。对于加强方法中引用目标对象中方法参数时,参数名称则能够随意定义,不须要同切入点方法参数一致。
若是你很任性,在@Around中就不想用以前的参数名称,怎么办?使用@Around中的argNames属性,它同xml配置中的arg-names相对应。
@Around(value="pointcut(music,datetime)",argNames="music,datetime") public Object aroundIntercept(ProceedingJoinPoint pjp, String musi, Date date) throws Throwable{ Object retVal = pjp.proceed(); return retVal; }
经过在argNames中定义参数名的昵称,从而在value中引用的pointcut方法便可使用昵称。可是我任务此举也绝对是任性的人才会玩的,好比我就是个任性的人,否则怎么会测试这种配置:-D。
对于args中定义方法入参类型的方式,我以前对它和直接在execution表达式中定义方法入参类型,这两种方式,感到很疑惑。
来看下execution表达式定义方法入参类型的方式
<aop:pointcut id="pointcut" expression="execution(* com.lcifn.spring.aop.bean.*.*(String,java.util.Date,..))"/>
对应的加强方法则是
public Object aroundIntercept(ProceedingJoinPoint pjp){}
通过个人反复测试,execution定义方法入参类型同args的方式有如下的区别:
execution定义方法入参类型的注解方式则是沿用xml的规则
@Component @Aspect public class AspectJAnnotationArgsBrowserAroundAdvice { @Pointcut("execution(* com.lcifn.spring.aop.bean.ChromeBrowser.*(String,java.util.Date+,..))") private void pointcut(){ } @Around(value="pointcut()") public Object aroundIntercept(ProceedingJoinPoint pjp) throws Throwable{ Object retVal = pjp.proceed(); return retVal; } }
在切入点方法pointcut中也是不容许定义参数,而仅仅经过execution表达式中定义的方法入参类型进行匹配。
对于args和arg-names,除非有比较特殊的需求,否则基本也不会使用。做为任性的我,看到网上一波波介绍Spring expression表达式时都会copy这两个的用法,就在看源码的同时特地对它们进行了测试,所以留下这篇给本身和他人之后万一使用时作个参考。
然而在我测试的过程,另外一个问题引发了个人关注(上文中有提到),在@Pointcut中定义args(music,date)时,pointcut方法里的两个参数的名称也必须同其一致,否则则抛错。这引发个人很大兴趣,由于在以前也看过一些博文,对于java中的方法参数名正常的方式是获取不到的,那此处是怎么获取到方法参数名的呢,请见下一章。