定義切入點java
在前文(點擊查看)中使用到的AdviceTest類中同一個切點(即* com.abc.service.*.advice*(..)匹配的鏈接點)卻重複定義了屢次,這顯然不符合軟件設計的原則,爲了解決這個問題,AspectJ和Spring都提供了切入點的定義。所謂定義切入點,其實質就是爲一個切入點表達式起一個名稱,從而容許在多個加強處理中重用該名稱。
編程
Spring AOP只支持以Spring Bean的方法執行組做爲鏈接點,因此能夠把切入點看做全部能和切入表達式匹配的Bean方法。切入點定義包含兩個部分:
框架
一個切入點表達式:用於指定切入點和哪些方法進行匹配this
一個包含名字和任意參數的方法簽名:將做爲切入點的名稱spa
在@AspectJ風格的AOP中,切入點簽名採用一個普通的方法定義(方法體一般爲空)來提供(方法名即爲切點名),且該方法的返回值必須爲void,切入點表達式需使用@Pointcut註解來標註。下面的代碼片斷定義了一個切入點,這個切入點將匹配任何名爲transfer的方法的執行:.net
//使用@Pointcut註解時指定切入點表達式 @Pointcut("execution(* transfer(..))") //使用一個返回值爲void,方法體爲空的方法來命名切入點,方法名即爲切點名 private void myPointcut(){}
切入點表達式,也就是組成@Pointcut註解的值,是規範的AspectJ 5切入點表達式。若是想要了解更多的關於AspectJ切入點語言,請參見AspectJ編程指南。
設計
一旦採用上面的代碼片斷定義了名爲myPointcut的切入點以後,程序就能夠屢次重複使用該切點了,甚至能夠在其餘切面類、其餘包的切面類裏使用該切點,至因而否能夠在其餘切面類、其餘包下使用這個切點,那就要看該方法前的訪問控制修飾符了——本例中myPointcut使用private修飾,則意味着僅能在當前切面類中使用這個切點。
代理
若是須要使用本切面類中的切點,則可在使用@Pointcut註解時,指定value屬性值爲已有的切入點,以下:
code
@AfterReturning(pointcut="myPointcut()", returning="returnValue") public void log(String message, Object returnValue) { //do something... }
從指定pointcut來看,其語法很是相似於Java中調用方法——只是該方法表明一個切點,其實質是爲該加強處理方法定義一個切入點表達式。若是須要使用其餘類中定義的切點,則定義這些切點的方法的修飾符不能爲private。如今假設在另外一個類PointcutDefinition中定義了一個名爲myPointcutTest的切點:對象
public class PointcutDefinition { @Pointcut("execution(* something(..))") //訪問控制符爲public,這個切點能夠在其餘任何地方引用 public void myPointcutTest(){} }
則在引用的時候須要帶上類名,例如:
@AfterReturning( pointcut="PointcutDefinition.myPointcutTest() && args(message)", returning="returnValue") public void log(String message, Object returnValue) { //do something... }
切入點指示符
前面定義切點表達式時使用了大量的execution表達式,其中execution就是一個切入點指示符。Spring AOP僅支持部分AspectJ的切入點指示符,但Spring AOP還額外支持一個bean切入點指示符。不只如此,由於Spring AOP只支持使用方法調用做爲鏈接點,因此Spring AOP的切入點指示符僅匹配方法執行的鏈接點。
完整的AspectJ切入點語言支持大量切入點指示符,可是Spring並不支持它們。它們是:call,get,preinitialization,staticinitialization,initialization,handler,adviceexecution,withincode,cflow,cflowbelow,if,@this和@withincode。一旦在Spring AOP中使用這些切點指示符,就會拋出IllegalArgumentException。
Spring AOP支持的切入點指示符有以下幾個:
execution:用於匹配執行方法的鏈接點,這是Spring AOP中國最主要的切入點指示符。該切入點的用法也相對複雜,execution表達式的格式以下:
execution(modifier-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
上面的格式中,execution是不變的,用於做爲execution表達式的開頭,整個表達式中幾個參數的詳細解釋以下:
modifier-pattern:指定方法的修飾符,支持通配符,該部分能夠省略
ret-type-pattern:指定返回值類型,支持通配符,可使用「*」來通配全部的返回值類型
declaring-type-pattern:指定方法所屬的類,支持通配符,該部分能夠省略
name-pattern:指定匹配的方法名,支持通配符,可使用「*」來通配全部的方法名
param-pattern:指定方法的形參列表,支持兩個通配符,「*」和「..」,其中「*」表明一個任意類型的參數,而「..」表明0個或多個任意類型的參數。
throw-pattern:指定方法聲明拋出的異常,支持通配符,該部分能夠省略
以下是幾個execution表達式:
execution(public * * (..))//匹配全部public方法
execution(* set*(..))//匹配以set開始的方法
execution(* com.abc.service.AdviceManager.* (..))//匹配AdviceManager中任意方法
execution(* com.abc.service.*.* (..))//匹配com.abc.servcie包中任意類的任意方法
within:限定匹配特定類型的鏈接點,當使用Spring AOP的時候,只能匹配方法執行的鏈接點。下面是幾個例子:
within(com.abc.service.*)//匹配com.abc.service包中的任意鏈接點
within(com.abc.service..*)//匹配com.abc.service包或子包中任意的鏈接點
this:用於指定AOP代理必須是指定類型的實例,用於匹配該對象的全部鏈接點。當使用Spring AOP的時候,只能匹配方法執行的鏈接點。下面是個例子:
this(com.abc.service.AdviceManager)//匹配實現了AdviceManager接口的代理對象的全部鏈接點,在Spring中只是方法執行的鏈接點
target:用於限定目標對象必須是指定類型的實例,用於匹配該對象的全部鏈接點。當使用Spring AOP的時候,只能匹配方法執行的鏈接點。下面是個例子:
target(com.abc.servcie.AdviceManager)//匹配實現了AdviceManager接口的目標對象的全部鏈接點,在Spring中只是方法執行的鏈接點
args:用於對鏈接點的參數類型進行限制,要求參數的類型時指定類型的實例。一樣,當使用Spring AOP的時候,只能匹配方法執行的鏈接點。下面是個例子:
args(java.io.Serializable)//匹配只接受一個參數,且參數類型是Serializable的全部鏈接點,在Spring中只是方法執行的鏈接點
注意,這個例子與使用execution(* *(java.io.Serializable))定義的切點不一樣,args版本只匹配運行時動態傳入參數值是Serializable類型的情形,而execution版本則匹配方法簽名只包含一個Serializable類型的形參的方法。
另外,Spring AOP還提供了一個名爲bean的切入點提示符,它是Spring AOP額外支持的,並非AspectJ所支持的切入點指示符。這個指示符對Spring框架來講很是有用:它將指定爲Spring中的哪一個Bean織入加強處理。固然,Spring AOP中只能使用方法執行做爲鏈接點。
bean:用於指定只匹配該Bean實例內的鏈接點,實際上只能使用方法執行做爲鏈接點。定義bean表達式時須要傳入Bean的id或name,支持使用"*"通配符。下面是幾個例子:
bean(adviceManager)//匹配adviceManager實例內方法執行的鏈接點
bean(*Manager)//匹配以Manager結尾的實例內方法執行的鏈接點
使用組合切點表達式
Spring支持使用以下三個邏輯運算符來組合切入點表達式:
&&:要求鏈接點同時匹配兩個切點表達式
||:要求鏈接點匹配至少一個切入點表達式
!:要求鏈接點不匹配指定的切入點表達式
其實在以前介紹args的時候,已經用到了「&&」運算符:
pointcut("execution(* com.abc.service.*.*(..) && args(name))")
上面的pointcut由兩個表達式組成,並且使用&&來組合這兩個表達式,所以鏈接點須要同時知足這兩個表達式才能被織入加強處理。