定義切入點
在前文(點擊查看)中使用到的AdviceTest類中同一個切點(即* com.abc.service.*.advice*(..)匹配的連接點)卻重復定義了多次,這顯然不符合軟件設計的原則,為了解決這個問題,AspectJ和spring都提供了切入點的定義。所謂定義切入點,其實質就是為一個切入點表達式起一個名稱,從而允許在多個增強處理中重用該名稱。
Spring AOP只支持以Spring Bean的方法執行組作為連接點,所以可以把切入點看作所有能和切入表達式匹配的Bean方法。切入點定義包含兩個部分:
-
一個切入點表達式:用於指定切入點和哪些方法進行匹配
-
一個包含名字和任意參數的方法簽名:將作為切入點的名稱
在@AspectJ風格的AOP中,切入點簽名采用一個普通的方法定義(方法體通常為空)來提供(方法名即為切點名),且該方法的返回值必須為void,切入點表達式需使用@Pointcut注解來標注。下面的代碼片段定義了一個切入點,這個切入點將匹配任何名為transfer的方法的執行:
|
1
2
3
4
|
//使用@Pointcut注解時指定切入點表達式
@Pointcut
(
"execution(* transfer(..))"
)
//使用一個返回值為void,方法體為空的方法來命名切入點,方法名即為切點名
private
void
myPointcut(){}
|
切入點表達式,也就是組成@Pointcut注解的值,是規范的AspectJ 5切入點表達式。如果想要了解更多的關於AspectJ切入點語言,請參見AspectJ編程指南。
一旦采用上面的代碼片段定義了名為myPointcut的切入點之后,程序就可以多次重復使用該切點了,甚至可以在其他切面類、其他包的切面類里使用該切點,至於是否可以在其他切面類、其他包下使用這個切點,那就要看該方法前的訪問控制修飾符了——本例中myPointcut使用private修飾,則意味着僅能在當前切面類中使用這個切點。
如果需要使用本切面類中的切點,則可在使用@Pointcut注解時,指定value屬性值為已有的切入點,如下:
|
1
2
3
4
|
@AfterReturning
(pointcut=
"myPointcut()"
, returning=
"returnValue"
)
public
void
log(String message, Object returnValue) {
//do something...
}
|
從指定pointcut來看,其語法非常類似於Java中調用方法——只是該方法代表一個切點,其實質是為該增強處理方法定義一個切入點表達式。如果需要使用其他類中定義的切點,則定義這些切點的方法的修飾符不能為private。現在假設在另一個類PointcutDefinition中定義了一個名為myPointcutTest的切點:
|
1
2
3
4
5
|
public
class
PointcutDefinition {
@Pointcut
(
"execution(* something(..))"
)
//訪問控制符為public,這個切點可以在其他任何地方引用
public
void
myPointcutTest(){}
}
|
則在引用的時候需要帶上類名,例如:
|
1
2
3
4
5
6
|
@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的時候,已經用到了“&&”運算符:
|
1
|
pointcut(
"execution(* com.abc.service.*.*(..) && args(name))"
)
|
上面的pointcut由兩個表達式組成,而且使用&&來組合這兩個表達式,因此連接點需要同時滿足這兩個表達式才能被織入增強處理。
http://blog.csdn.net/caomiao2006/article/details/51287204
