Spring AOP概念及切入點

 1,概念
面向切面編程是對程序OOP編程的另一種補充。OO將應用程序分解爲對象層次,而AOP則將程序分解爲各個方面或者關係。這就使得模塊之間的關聯能夠跨多個對象進行處理,例如事務管理(在術語上被稱爲"橫切")。
Spring中一個重要的組成部分就是AOP框架。如果Spring的IoC容器(BeanFactory和ApplicationContext)不依賴於AOP,也就意味着你不需要使用AOP。AOP爲Spring IoC提供了一個非常能幹的中間件解決方案。

AOP在Spring中的使用:
提供公佈式企業級服務,特別是替代EJB公佈式服務。最重要的是這種服務是公佈式事務管理,建立在Spring的事務抽象上。
允許使用者實現自定義切面,對自己的OOP補充AOP。

因此你可以將Spring AOP看作一種可以允許Spring提供公佈式事務管理的技術,而這種技術並不需要EJB;或者使用Spring的AOP框架來實現自己的切面。

1.1,AOP概念
讓我們先了解一些AOP的概念和定義。這些術語並不是Spring特有。不幸的是,AOP術語並不顯得直觀。如果Spring使用它自己的術語,那麼就更顯得混淆。
Aspect(切面):一種跨多對象的橫向模塊化關係。J2EE應用程序中的事務管理就是一個很好的橫切關係的例子。切面作爲顧問和攔截應用於Spring中。
Joinpoint(接合點):程序執行過程中的點,就好象方法中的一個調用,或者一個特別的被拋出的異常。在Spring AOP中,一個接合點通常是方法調用。Spring並不明顯地使用"接合點"作爲術語;接合點信息可以通過MethodInvocation的參數穿過攔截器訪問,相當於實現org.springframework.aop.Poinkcut接口。
Advice(通知):被AOP框架使用的特定接合點。不同的通知類型包括:"around","before"和"throws"通知。通知類型在下面介紹。許多AOP框架,包括Spring,通知模塊就如同一個攔截器,維持一系列攔截器圍繞着接合點。
Pointcut(切入點):當一個通知將被激活的時候,會指定一些結合點。一個AOP框架必須允許開發者指定切入點:例如使用正則表達式。
Introduction(引入):向一個通知類中添加方法或成員變量。Spring允許你向任何通知類中引入新接口。例如,你可能會使用一個引入,以使得任何實現IsModified接口的對象簡單的緩存。
Target object(標籤對象):包含接合點的對象。也被通知對象或者代理對象所引用。
AOP proxy(AOP代理):被AOP框架創建的對象,包括通知。在Spring中,一個AOP代理將會是一個JDK動態代理或者一個CGLIB代理。
Weaving(編織):聚合切面以生成一個通知對象。這可以在編譯時(比如使用AspectJ編譯器)或在運行時完成。Spring,如同其它純Java AOP框架一樣,在運行時完成編織。

通知類型介紹:
Around advice(圍繞通知):通知圍繞一個接合點(如一個方法調用)。這是最強有力的通知類型。圍繞通知將會在方法調用之前或之後完成自定義行爲。他們能夠選擇是否通過接合點,或者返回他們自己特定的值,甚至拋出異常。
Before advice(提前通知):通知在接合點之前執行,但是並卻並沒有能力阻止流程執行接合點(如果不拋出異常的話)。
Throws advice(拋出通知):如果一個方法拋出異常,通知將會被執行。Spring提供了健壯的類型以拋出通知,所以你可以按自己的喜好編寫代碼捕捉異常(和子類),而不需要通過Throwable或Exception。
After returning advice(返回通知):在接點正常完成之後,通知將會被執行。例如,如果一個方法返回卻沒有拋出異常。

圍繞通知是通知中最普遍的類型。大多數基於AOP框架的攔截器,如Nanning Aspects,只提供了圍繞通知。
而Spring,如同AspectJ,提供了所有通知類型。我們建議你使用,能夠滿足你需要的最低消耗通知類型。例如,如果你僅僅需要通過一個方法的返回值來更新緩存,那麼你最好實現"返回通知",而不是"圍繞通知",雖然圍繞通知能夠完成相同需求。使用高效的通知類型能夠提供一個簡單的程序模塊,以減少潛在的錯誤。例如,你並不需要調用proceed()方法,該方法在基於圍繞通知中的MethodInvocation使用。
切入點是AOP的關鍵,區別於AOP舊版本提供攔截器的技術。切入點能夠被不同層次的OO通知觸發。例如,一個圍繞通知提供了公佈式事務管理能夠被應用於一系列方法,以生成若干對象。因此,切入點作爲AOP的結構要素。

1.2,Spring AOP性能和目標
Spring AOP用於純Java中。並不需要特殊編譯過程。Spring AOP並不需要控制類裝載層,並且因此適合應用於J2EE web容器,或者應用程序服務。
Spring現在支持方法調用攔截器。雖然增加成員變量攔截器並不會破壞Spring AOP APIs代碼,但是成員變量攔截器並沒有實現。(成員變量攔截器被證明是違背OO封裝的,我們相信在應用程序中沒有成員變量攔截器,並不是不好。如果你需要成員變量攔截器,可以考慮使用AspectJ)。
Spring提供了一些類以表現切入點和不同類型的通知。Spring使用術語:通知者,表示一個對象表現一個切面,包括一個通知和一個指定接合點的觸發切入點。
不同的通知類型都是MethodInterceptor(來自the AOP Alliance interception API);而通知接口均在org.springframework.aop包中定義。所有通知必須實現org.apoalliance.aop.Advice接口。
Spring實現AOP Alliance攔截接口(http://www.sourceforge.net/projects/aopalliance),圍繞通知必須實現AOP Alliance的org.apoalliance.intercpet.MethodInterceptor接口。實現這個接口能夠在Spring中運行,或者任何其他AOP Alliance合適的執行。現在的JAC實現AOP Alliance接口,而Nanning和Dynaop可能在2004年早些時候。
Spring的方式不同於大多數其它AOP框架。Spring的目標不是提供一個完全的AOP實現(雖然Spring的AOP很能幹)。Spring提供了一個AOP實現與Spring IoC之間的綜合,以幫助解決在企業級中的大多數問題。
因此,例如,Spring的AOP普遍用於Spring IoC容器之間的連接。AOP通知是特定用於普通bean定義語法(雖然這允許強有力的"autoproxying"性能)。通知和接合點都是通過Spring IoC管理:相比於其他OAP實現,這是一個至關重要的區別。有些東西在Spring AOP中並不容易使用,效率也不高。例如高細粒度對象。在這種情況下,AspectJ是最好的選擇。儘管以我們的經驗,對於服從AOP的J2EE應用,Spring AOP提供了一個極好的解決方案。
Spring AOP將不會致力於提供一個完善全面的AOP解決方案,以和AspectJ或AspectWerkz競爭。我們相信那兩種基於代理的框架像Spring與AspectJ都是有價值的,相互補充的,而不是競爭的關係。因此Spring1.1的一個主要目標,就是使得Spring AOP和IoC能夠和AspectJ無縫隙地融合,以便使得基於Spring的應用程序結構能夠使用所有的AOP功能,並有很好的協調性。這個融合並不會影響Spring AOP API或者AOP Alliance API,Spring AOP將會考慮對以前版本的兼容性。

1.3,Spring中的AOP代理
Spring默認使用J2SE的動態代理來處理AOP代理。這使得任何一個或一堆接口能夠被代理。
Spring同樣可以使用CGLIB代理。代理類比代理接口更有必要。CGLIB默認被用於業務對象不能夠實現接口的情況。因爲業務對象通常會顯示一個或多個業務接口。
也可能會強迫使用CGLIB:我們會在下面討論,並解釋爲什麼你並不想這樣做的原因。在Spring1.0之前,Spring可能提供額外的AOP代理類型,包括整個生成類。這並不會影響程序模型。

2,Spring中的切入點
讓我們來看看Spring怎樣操作至關重要的切入點概念。

2.1,概念
Spring的切入點模型能夠使切入點不依賴通知類型而重複使用。可能同一個切入點會觸發不同的通知。
org.springframework.aop.Pointcut接口是中心,用於特定的類和方法觸發通知。完整的接口如下:

public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}

將Pointcut接口分離爲兩個部分,允許類的重複使用和方法的匹配,並能夠高細粒度合成處理(就如同與另外一個方法的匹配器完成一個"聯合")。
ClassFilter接口通過提供一些觸發類,被用於約束切入點。如果matchs()方法總是返回true,那麼所有的觸發類將會被匹配:

public interface ClassFilter {
boolean matches(Class class);
}

MethodMatcher接口通常更重要,完整接口如下:

public interface MethodMatcher {
boolean matcher(Method m, Class targetClass) ;
boolean isRuntime();
boolean matches(Method m, Class targetClass, Object[] args);
}

matches(method, class)方法被用來測試是否這個切入點在觸發類中會被給定的方法匹配。這種賦值當AOP代理被創建的時候可以被執行,以避免對每一個方法調用進行測試。對給予的方法,如果兩個參數的匹配方法返回true,並且在使用MethodMatcher時,isRuntime()方法返回true,則三個參數的匹配方法將會在每個方法調用時被調用。這樣就能夠使得切入點,在觸發通知被執行之前,直接通過參數傳遞給方法調用。
大多數MethodMatcher是靜態的,意味着他們的isRuntime()返回false。這種情況下,三個參數的匹配方法將不會被調用。(如果可能,試着將切入點標爲靜態,使得當一個AOP代理被創建的時候,允許AOP框架緩存切入點所賦的值)。

2.2,切入點的運作
Spring支持三種切入點的運作:顯著,聯合和交集。
聯合表示多個方法被任一切入點匹配。
攔截表示多個方法被兩個切入點匹配。
聯合通常更有用。
多個切入點可以使用org.springframework.aop.support.Pointcuts類通過靜態方法組織起來,也可以使用同一個包下應用程序的其他特殊切入點類的子類。

2.3,方便地實現切入點
Spring提供了幾種方便的方式實現切入點。其中一些方式是黑盒子式,另外的則需要繼承指定的應用程序切入點類。

2.3.1,靜態切入點
靜態切入點基於方法及觸發類,並且不需要考慮方法的參數。靜態切入點對大多數使用來說,更好更高效。當方法被調用的時候,Spring可能只需要給靜態切入點賦一次值。之後,當方法被再次調用的時候,不需要再次賦值。
讓我們來看看Spring中集中靜態切入點的實現:

2.3.1.1,正則表達式
正則表達式是一種顯而易見的方式,以指定靜態切入點。一些AOP框架(除了Spring)支持這種方式。org.springframework.apo.support.RegexpMethodPointcut是一種普遍的正則表達式切入點,使用Perl5正則表達式規則。
使用這個類,你可以提供一個正則字符串的list。如果其中任何一個正則字符串被匹配,那麼切入點將會被賦值爲true。


  
.*get.*
.*absquatulate
  


而一個RegexpMethodPointcut和RegexpMethodPointcutAdvisor的子類,可以使我們方便地引用一個通知(記住,通知是一個攔截器,可能是"提前通知",或者"拋出通知"等)。下面是一個將bean servers與切入點和通知者一起綁定的示例:


  


  
.*get.*
.*absqustulate
  


任何類型的通知都可以使用ReexpMethodPointcutAdvisor,而RegexpMethodPointcut類要求Jakarta ORO正則表達式包。

2.3.1.2,切入點屬性控制
一種重要的靜態切入點類型是metadata-driven(控制元數據)切入點。它使用元數據屬性:典型的方式是多層元數據。

2.3.2,動態切入點
賦值動態切入點的消耗比靜態切入點大。動態切入點作爲方法的參數,也是靜態信息。這意味着它們必須在方法每次被調用時賦值,且方法返回的結果不能夠被緩存,因爲參數的值是不停變化的。
典型的示例是控制流切入點:

2.3.2.1,控制流切入點
Spring控制流切入點在概念上與Aspect控制流切入點相似,但是Aspect控制流切入點更強大(Spring控制流切入點無法在一個切入點執行完之前指定另外一個切入點)。一個控制流切入點匹配當前的返回值。比如,可能當一個接入點在com.mycompany.web包中被一個方法或者SomeCaller類調用時,控制流切入點被觸發。控制流切入點通過org.springframework.aop.support.ControlFlowPointcut類指定。
注意:控制流切入點比其它動態切入點消耗更大,在Java 1.4中,它是其他動態切入點的5被消耗,而在Java 1.3中,是10倍。

2.4 切入點的父類
Spring提供非常有用的切入點父類,以幫助你實現自己的切入點。
因爲靜態切入點更高效,你可以使用StaticMethodMatcherPointcut作爲父類,如下示例。使用這個類需要實現一個抽象方法。
class TestStaticPointcut extents StaticMethodMatcherPointcut {
public boolean matches(Method m, Class targetClass) {
   // return true if custom criteria match
}
}
當然,Spring也提供了其它方式的父類。你可以在Spring 1.0RC2中使用任何通知類型的自定義切入點。

2.5,自定義切入點

因爲Spring中切入點都是Java類,而不是語言特徵(如在AspectJ中)。切入點的聲明可以是靜態的,或者動態的。儘管Spring沒有提供黑盒子似的像AspectJ標號那樣編碼的靈活切入點表達式。但是自定義切入點在Spring中可以任意組合。


來自:http://hi.baidu.com/comtpucbrfbprxr/item/2de127da051d9410d90e44fd

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章