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中的方法參數名正常的方式是獲取不到的,那此處是怎麼獲取到方法參數名的呢,請見下一章。

相關文章
相關標籤/搜索