?
This document uses PHP Chinese website manual Release
目錄
面向切面編程(AOP)通過提供另外一種思考程序結(jié)構(gòu)的途經(jīng)來彌補(bǔ)面向?qū)ο缶幊蹋∣OP)的不足。在OOP中模塊化的關(guān)鍵單元是類(classes),而在AOP中模塊化的單元則是切面。切面能對關(guān)注點(diǎn)進(jìn)行模塊化,例如橫切多個類型和對象的事務(wù)管理。(在AOP術(shù)語中通常稱作橫切(crosscutting)關(guān)注點(diǎn)。)
AOP框架是Spring的一個重要組成部分。但是Spring IoC容器并不依賴于AOP,這意味著你有權(quán)利選擇是否使用AOP,AOP做為Spring IoC容器的一個補(bǔ)充,使它成為一個強(qiáng)大的中間件解決方案。
AOP在Spring Framework中的作用
提供聲明式企業(yè)服務(wù),特別是為了替代EJB聲明式服務(wù)。最重要的服務(wù)是聲明性事務(wù)管理。
允許用戶實(shí)現(xiàn)自定義切面,用AOP來完善OOP的使用。
如果你只打算使用通用的聲明式服務(wù)或者封裝好的聲明式中間件服務(wù),例如緩沖池(pooling),那么你不必與Spring AOP直接打交道,并且本章的大部分內(nèi)容可以跳過了。
首先讓我們從一些重要的AOP概念和術(shù)語開始。這些術(shù)語不是Spring特有的。不過AOP術(shù)語并不是特別的直觀,如果Spring使用自己的術(shù)語,將會變得更加令人困惑。
切面(Aspect):一個關(guān)注點(diǎn)的模塊化,這個關(guān)注點(diǎn)可能會橫切多個對象。事務(wù)管理是J2EE應(yīng)用中一個關(guān)于橫切關(guān)注點(diǎn)的很好的例子。在Spring AOP中,切面可以使用基于模式)或者基于@Aspect注解的方式來實(shí)現(xiàn)。
連接點(diǎn)(Joinpoint):在程序執(zhí)行過程中某個特定的點(diǎn),比如某方法調(diào)用的時候或者處理異常的時候。在Spring AOP中,一個連接點(diǎn)總是表示一個方法的執(zhí)行。
通知(Advice):在切面的某個特定的連接點(diǎn)上執(zhí)行的動作。其中包括了“around”、“before”和“after”等不同類型的通知(通知的類型將在后面部分進(jìn)行討論)。許多AOP框架(包括Spring)都是以攔截器做通知模型,并維護(hù)一個以連接點(diǎn)為中心的攔截器鏈。
切入點(diǎn)(Pointcut):匹配連接點(diǎn)的斷言。通知和一個切入點(diǎn)表達(dá)式關(guān)聯(lián),并在滿足這個切入點(diǎn)的連接點(diǎn)上運(yùn)行(例如,當(dāng)執(zhí)行某個特定名稱的方法時)。切入點(diǎn)表達(dá)式如何和連接點(diǎn)匹配是AOP的核心:Spring缺省使用AspectJ切入點(diǎn)語法。
引入(Introduction):用來給一個類型聲明額外的方法或?qū)傩裕ㄒ脖环Q為連接類型聲明(inter-type declaration))。Spring允許引入新的接口(以及一個對應(yīng)的實(shí)現(xiàn))到任何被代理的對象。例如,你可以使用引入來使一個bean實(shí)現(xiàn)IsModified
接口,以便簡化緩存機(jī)制。
目標(biāo)對象(Target Object): 被一個或者多個切面所通知的對象。也被稱做被通知(advised)對象。 既然Spring AOP是通過運(yùn)行時代理實(shí)現(xiàn)的,這個對象永遠(yuǎn)是一個被代理(proxied)對象。
AOP代理(AOP Proxy):AOP框架創(chuàng)建的對象,用來實(shí)現(xiàn)切面契約(例如通知方法執(zhí)行等等)。在Spring中,AOP代理可以是JDK動態(tài)代理或者CGLIB代理。
織入(Weaving):把切面連接到其它的應(yīng)用程序類型或者對象上,并創(chuàng)建一個被通知的對象。這些可以在編譯時(例如使用AspectJ編譯器),類加載時和運(yùn)行時完成。Spring和其他純Java AOP框架一樣,在運(yùn)行時完成織入。
通知類型:
前置通知(Before advice):在某連接點(diǎn)之前執(zhí)行的通知,但這個通知不能阻止連接點(diǎn)之前的執(zhí)行流程(除非它拋出一個異常)。
后置通知(After returning advice):在某連接點(diǎn)正常完成后執(zhí)行的通知:例如,一個方法沒有拋出任何異常,正常返回。
異常通知(After throwing advice):在方法拋出異常退出時執(zhí)行的通知。
最終通知(After (finally) advice):當(dāng)某連接點(diǎn)退出的時候執(zhí)行的通知(不論是正常返回還是異常退出)。
環(huán)繞通知(Around Advice):包圍一個連接點(diǎn)的通知,如方法調(diào)用。這是最強(qiáng)大的一種通知類型。環(huán)繞通知可以在方法調(diào)用前后完成自定義的行為。它也會選擇是否繼續(xù)執(zhí)行連接點(diǎn)或直接返回它自己的返回值或拋出異常來結(jié)束執(zhí)行。
環(huán)繞通知是最常用的通知類型。和AspectJ一樣,Spring提供所有類型的通知,我們推薦你使用盡可能簡單的通知類型來實(shí)現(xiàn)需要的功能。例如,如果你只是需要一個方法的返回值來更新緩存,最好使用后置通知而不是環(huán)繞通知,盡管環(huán)繞通知也能完成同樣的事情。用最合適的通知類型可以使得編程模型變得簡單,并且能夠避免很多潛在的錯誤。比如,你不需要在JoinPoint
上調(diào)用用于環(huán)繞通知的proceed()
方法,就不會有調(diào)用的問題。
在Spring 2.0中,所有的通知參數(shù)都是靜態(tài)類型,因此你可以使用合適的類型(例如一個方法執(zhí)行后的返回值類型)作為通知的參數(shù)而不是使用Object
數(shù)組。
通過切入點(diǎn)匹配連接點(diǎn)的概念是AOP的關(guān)鍵,這使得AOP不同于其它僅僅提供攔截功能的舊技術(shù)。 切入點(diǎn)使得通知可以獨(dú)立對應(yīng)到面向?qū)ο蟮膶哟谓Y(jié)構(gòu)中。例如,一個提供聲明式事務(wù)管理 的環(huán)繞通知可以被應(yīng)用到一組橫跨多個對象的方法上(例如服務(wù)層的所有業(yè)務(wù)操作)。
Spring AOP使用純Java實(shí)現(xiàn)。它不需要專門的編譯過程。Spring AOP不需要控制類裝載器層次,因此它適用于J2EE web容器或應(yīng)用服務(wù)器。
Spring目前僅支持使用方法調(diào)用作為連接點(diǎn)(join point)(在Spring bean上通知方法的執(zhí)行)。雖然可以在不影響到Spring AOP核心API的情況下加入對成員變量攔截器支持,但Spring并沒有實(shí)現(xiàn)成員變量攔截器。如果你需要把對成員變量的訪問和更新也作為通知的連接點(diǎn),可以考慮其它的語言,如AspectJ。
Spring實(shí)現(xiàn)AOP的方法跟其他的框架不同。Spring并不是要提供最完整的AOP實(shí)現(xiàn)(盡管Spring AOP有這個能力),相反的,它其實(shí)側(cè)重于提供一種AOP實(shí)現(xiàn)和Spring IoC容器之間的整合,用于幫助解決在企業(yè)級開發(fā)中的常見問題。
因此,Spring的AOP功能通常都和Spring IoC容器一起使用。切面使用普通的bean定義語法來配置(盡管Spring提供了強(qiáng)大的"自動代理(autoproxying)"功能):與其他AOP實(shí)現(xiàn)相比這是一個顯著的區(qū)別。有些事使用Spring AOP是無法輕松或者高效完成的,比如說通知一個細(xì)粒度的對象(例如典型的域?qū)ο螅哼@種時候,使用AspectJ是最好的選擇。不過經(jīng)驗(yàn)告訴我們,對于大多數(shù)在J2EE應(yīng)用中適合用AOP來解決的問題,Spring AOP都提供了一個非常好的解決方案。
Spring AOP從來沒有打算通過提供一種全面的AOP解決方案來與AspectJ競爭。我們相信無論是基于代理(proxy-based)的框架如Spring AOP或者是成熟的框架如AspectJ都是很有價值的,他們之間應(yīng)該是互補(bǔ)而不是競爭的關(guān)系。Spring 2.0可以無縫的整合Spring AOP,IoC和AspectJ,使得所有的AOP應(yīng)用完全融入基于Spring的應(yīng)用體系。這樣的集成不會影響Spring AOP API或者AOP Alliance API;Spring AOP保持了向下兼容性。下一章會詳細(xì)討論Spring AOP的API。
Spring Framework一個重要的原則就是無侵入性(non-invasiveness); 這個思想指你不應(yīng)當(dāng)被迫引入框架特定的類和接口到你的業(yè)務(wù)/領(lǐng)域模型中。然而,Spring Framework在某些地方給你一個是否引入Spring框架特定依賴到你的代碼的選項(xiàng): 給你這個選項(xiàng)的理由是因?yàn)樵谔囟ǖ膱鼍爸兴赡軆H僅是容易閱讀或用這種方法編寫特定的功能塊。Spring Framework(幾乎)一直會為你提供這種選擇:從而使你能做出一個明智的決定,使它最適應(yīng)你的特定用例或場景。
你可以選擇AspectJ或者Spring AOP,以及選擇是使用@AspectJ注解風(fēng)格還是Spring XML配置風(fēng)格。事實(shí)上本章選擇先介紹@AspectJ風(fēng)格的方法不應(yīng)當(dāng)被看作是這樣一個暗示:Spring小組喜歡@AspectJ注解風(fēng)格更勝于Spring XML配置。
在第?6.4?節(jié) “AOP聲明風(fēng)格的選擇”一章有對使用各個風(fēng)格理由的一個更全面的討論。
Spring缺省使用J2SE 動態(tài)代理(dynamic proxies)來作為AOP的代理。 這樣任何接口(或者接口集)都可以被代理。
Spring也可以使用CGLIB代理. 對于需要代理類而不是代理接口的時候CGLIB代理是很有必要的。如果一個業(yè)務(wù)對象并沒有實(shí)現(xiàn)一個接口,默認(rèn)就會使用CGLIB。作為面向接口編程的最佳實(shí)踐,業(yè)務(wù)對象通常都會實(shí)現(xiàn)一個或多個接口。但也有可能會強(qiáng)制使用CGLIB,在這種情況(希望不常有)下,你可能需要通知一個沒有在接口中聲明的方法,或者需要傳入一個代理對象給方法作為具體類型
為了明白Spring AOP是基于代理(proxy-based)的事實(shí),請參閱第?6.6.1?節(jié) “理解AOP代理”。