Spring 2.0和Spring AOP
在Spring 2.0中最激動人心的增強之一是關于Spring AOP,它變得更加便于使用而且更加強大,主要是通過復雜而成熟的AspectJ語言的支持功能來實現,而同時保留純的基于代理的Java運行時。
我們一直堅信AOP(面向切面編程)很重要。為什么?因為它提供給我們一種新的思考程序結構的方法,能夠解決很多純OOP無法解決的問題——讓我們能夠在一個模塊中實現某些需求,而不是以發散的方式實現。
為了理解這些好處,讓我們考慮一些我們可以在需求中表達但無法直接用純OO代碼實現的情況。企業開發者使用一個通常的詞匯表來讓他們進行清楚的溝通。比如,像服務層,DAO層,Web層或者Web控制器這樣的術語,這不需要什么解釋。
許多需求是用這個詞匯表中的術語來表達的。比如:
◆服務層應該是可以處理事務的。
◆當DAO操作失敗時,SQLException或者其他特殊持久化技術的異常應該被翻譯,以確保DAO接口不會有漏掉的抽象。
◆服務層對象不應該調用Web層,因為各層應該只依賴直接處在其下方的層。
◆由于并發相關操作的失敗而導致失敗的等冪業務服務可以重試。
雖然這些需求都是現實存在的,并來自于經驗,但它們并不能用純OOP來優雅地解決。為什么?主要有兩個原因:
◆這些來自于我們詞匯表的術語有意義,但它們并不是抽象。我們不能使用術語編程;我們需要抽象。
◆所有這些都是所謂橫切關注點的例子。一個橫切關注點,在用傳統OO方法實現時,會分解成很多類和方法。比如,想象一下在跨DAO層遭遇特殊異常時要使用重試邏輯。這個關注點橫切許多DAO方法,而且在傳統的方式中會需要實現許多單獨的修改。
AOP就是通過對橫切關注點進行模塊化,并讓我們從普通的還可以編程的抽象的詞匯表來表達術語,來解決這樣問題的技術,這些抽象叫做切入點,我很快會再解釋一些關于它們的細節。這種方法帶來一些主要好處,比如:
◆因為減少了剪切粘貼風格的復制而減少代碼行數。這在像異常轉換和性能監測這樣的try/catch/finally習慣用法中尤其有效。
◆在單個代碼模塊中捕捉這樣需求的能力,提升可追蹤能力。
◆在單個地方修補bug的能力,而不需要重新訪問應用程序中許多位置。
◆確保橫切關注點不混淆主要的業務邏輯——隨著開發的進展,這很有可能成為危險之處。
◆開發者和團隊之間更好的職責分離。比如,重試功能可以有單個開發者或者團隊來編碼,而不需要由許多開發者跨多個子系統進行編碼。
因此AOP很重要,我們想提供***的解決方案。
Spring AOP無疑是最廣泛使用的AOP技術,歸功于以下優點:
◆采用成本幾近為零。
◆提供正確的切入點,這才稱得上是AOP而不僅僅是攔截。
◆提供一個支持許多使用方式的靈活的框架,可編程也可通過XML。
然而,在Spring 2.0之前,Spring中的AOP有一些缺點:
◆不寫Java代碼,只能表達簡單的切入點。并沒有一種切入點表達語言來以字符串形式,簡潔表達復雜的切入點,雖然RegexpMethodPointcutAdvisor允許定義簡單正規的基于表達的切入點。
◆當配置復雜AOP使用場景時,XML配置會變得很復雜。泛型元素被用來配置AOP類;雖然這對一致性來說很棒,對切面和類提供DI和其他服務,但它沒有一個專門的配置方法來得簡潔。
◆Spring AOP不適合通知細粒度的對象——對象需要由Spring管理或者通過編程被代理。
◆基于代理的方法的性能負載在少數案例中成為問題。
◆因為Spring AOP分離了代理和目標(被修飾或者被通知的對象),如果某個目標方法調用了目標上的方法,就不會使用到代理,意味著AOP通知并不適用。AOP使用基于代理的方法的正反面影響超出了本文的范圍:有一些積極的因素(比如能夠對同一個類的不同實例應用不同的通知),但主要還是消極的。
為了在Spring 2.0中增強這個重要領域,我們希望在它的優勢上構建,同時解決缺點。
***的兩個缺點也是最顯著的。它們都跟切入點相關。后面的三個缺點在Spring用戶的正常使用中很少發生,如果它們證明是的確有問題的,我們建議使用AspectJ。(就像你會看到的,這是Spring AOP直接的進步。)
XML配置擴展解決了關鍵的挑戰之一。因為我們想要保持Spring模塊的設計,我們過去不能在Spring DTD中提供特定于AOP的標簽——因此在這種情況下需要依賴可以詳細一點的通用配置。隨著Spring 2.0的出現,這樣的問題沒有了,因為XML schema并不像DTD,它允許擴展。我們可以提供一個AOP命名空間,看起來能讓Ioc容器識別AOP結構,但不會影響模塊化。
AOP術語101:理解切入點和通知
讓我們簡要地修正一下某些AOP術語。如果你使用過AOP這些概念,可能對你來說很熟悉——這些概念是相同的,僅僅有一點不同,即更加優雅和強大的表達方式。
切入點是匹配規則。它在程序執行中確定應該應用某個切面的點的集合。這些點叫做連接點。在應用程序運行時,連接點隨時會有,比如對象的實例化和方法的調用。在Spring AOP(所有版本)的案例中,唯一支持的連接點是公有方法的執行。
通知是可以被切面應用到連接點的行為。通知能在連接點之前或之后應用。通知的所有類型包括:
◆Before advice:在連接點之前調用的通知。比如,記錄方法調用即將發生的日志。
◆After returning adive:如果在連接點的方法正常返回時調用的通知。
◆AfterThrowing advice(在Spring1.x中叫做Throws通知):如果連接點的方法拋出一個特殊的異常時調用的通知。
◆After advice:在連接點之后調用的通知,無論結果是什么。特別像Java中的finally。
◆Around advice:能夠完全控制是否執行連接點的通知。比如,用來在事務中封裝某個方法調用,或者記錄方法的執行時間。
切面是結合切入點和通知成一個模塊方案,解決特殊的橫切問題。
如果這有點抽象,請不要擔心:代碼示例會很快解釋清楚的。
對在Spring 2.0和AspectJ的環境中關于AOP基礎的更深討論,請參考Adrian在InfoQ上很棒的文章,"Simplifying Enterprise Applications with Spring 2.0 and AspectJ."
為什么會是AspectJ切入點表達式?
迄今為止,我們討論過的概念都是基本的AOP概念,對于Spring AOP或者AspectJ而且這并不特別,在Spring1.x中已經是存在的。那么為什么我們選擇在Spring 2.0中采用AspectJ呢?
如果我們需要一種切入點表達語言,那么選擇就會很簡單。AspectJ有個思路很好,嚴格定義和充足文檔的切入點語言。它最近實現了一個當在Java 5上運行時,能對采用Java 5語法的編碼全面檢查。它不僅有很棒的參考材料,而且很多書籍和文章都對它進行了介紹。
我們不相信重新發明的輪子,而且定義我們自己的切入點表達語言是不合理的。進一步而言,自從AspectWerkz在2005年早期和冰島AspectJ項目之后,很明顯AspectJ是除了Spring 2.0之外唯一一個主流的AOP技術。因此關鍵的合并既是一種考慮也是一種技術優勢。
【編輯推薦】