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