Spring AOP中args和arg-names的使用

最近在看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

1.XML配置

根据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很差用,咱们来看看注解的方式。

2.注解

仍是以环绕加强举例,经过@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。

3.args和execution定义方法入参类型的区别

对于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的方式有如下的区别:

  1. execution定义方法入参类型只能根据入参类型匹配方法,而不能获取参数值。于是必须使用参数类型,如String,java.util.Date,不能使用args方式的昵称方式,且加强方法中不能引入方法参数。而args则是动态切入的,在每次方法执行时匹配一次,并获取参数值可供加强方法调用。
  2. execution定义的方法入参类型是彻底匹配,即定义的是java.util.Date,只能彻底匹配,其子类java.sql.Date也是匹配失败。若是同时匹配子类,可以使用+后缀符,即(String, java.util.Date+,..)能够匹配到java.sql.Date。而args的方式默认就是匹配自身及子类,即args(String, java.util.Date,..)是能够匹配到java.sql.Date的。

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表达式中定义的方法入参类型进行匹配。

4.总结

对于args和arg-names,除非有比较特殊的需求,否则基本也不会使用。做为任性的我,看到网上一波波介绍Spring expression表达式时都会copy这两个的用法,就在看源码的同时特地对它们进行了测试,所以留下这篇给本身和他人之后万一使用时作个参考。

然而在我测试的过程,另外一个问题引发了个人关注(上文中有提到),在@Pointcut中定义args(music,date)时,pointcut方法里的两个参数的名称也必须同其一致,否则则抛错。这引发个人很大兴趣,由于在以前也看过一些博文,对于java中的方法参数名正常的方式是获取不到的,那此处是怎么获取到方法参数名的呢,请见下一章。

相关文章
相关标签/搜索