1. 程式人生 > >Spring中的AOP(六)——定義切入點和切入點指示符

Spring中的AOP(六)——定義切入點和切入點指示符

定義切入點

    在前文(點選檢視)中使用到的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由兩個表示式組成,而且使用&&來組合這兩個表示式,因此連線點需要同時滿足這兩個表示式才能被織入增強處理。