名稱含義
Aspect Oriented Programming(AOP)是較為熱門的一個話題。AOP,國內大致譯作“面向切面編程”。
“面向切面編程”,這樣的名字並不是非常容易理解,且容易產生一些誤導。有些人認為“OOP/OOD11即將落伍,AOP是新一代軟體開發方式”。顯然,發言者並沒有理解AOP的含義。Aspect,的確是“方面”的意思。不過,漢語傳統語義中的“方面”,大多數情況下指的是一件事情的不同維度、或者說不同角度上的特性,比如我們常說:“這件事情要從幾個方面來看待”,往往意思是:需要從不同的角度來看待同一個事物。這裡的“方面”,指的是事物的外在特性在不同觀察角度下的體現。而在AOP中,Aspect的含義,可能更多的理解為“切面”比較合適。
可以通過預編譯方式和運行期動態代理實現在不修改原始碼的情況下給程式動態統一添加功能的一種技術。AOP實際是GoF設計模式的延續,設計模式孜孜不倦追求的是調用者和被調用者之間的解耦,提高代碼的靈活性和可擴展性,AOP可以說也是這種目標的一種實現。
在Spring中提供了面向切面編程的豐富支持,允許通過分離套用的業務邏輯與系統級服務(例如審計(auditing)和事務(transaction)管理)進行內聚性的開發。套用對象只實現它們應該做的——完成業務邏輯——僅此而已。它們並不負責(甚至是意識)其它的系統級關注點,例如日誌或事務支持。
主要功能
日誌記錄,性能統計,安全控制,事務處理,異常處理等等。
主要意圖
將日誌記錄,性能統計,安全控制,事務處理,異常處理等代碼從業務邏輯代碼中劃分出來,通過對這些行為的分離,我們希望可以將它們獨立到非指導業務邏輯的方法中,進而改變這些行為的時候不影響業務邏輯的代碼。
AOP/OOP
區分
AOP、OOP在字面上雖然非常類似,但卻是面向不同領域的兩種設計思想。OOP(面向對象編程)針對業務處理過程的實體及其屬性和行為進行抽象封裝,以獲得更加清晰高效的邏輯單元劃分。
而AOP則是針對業務處理過程中的切面進行提取,它所面對的是處理過程中的某個步驟或階段,以獲得邏輯過程中各部分之間低耦合性的隔離效果。這兩種設計思想在目標上有著本質的差異。
上面的陳述可能過於理論化,舉個簡單的例子,對於“雇員”這樣一個業務實體進行封裝,自然是OOP/OOD的任務,我們可以為其建立一個“Employee”類,並將“雇員”相關的屬性和行為封裝其中。而用AOP設計思想對“雇員”進行封裝將無從談起。
同樣,對於“許可權檢查”這一動作片斷進行劃分,則是AOP的目標領域。而通過OOD/OOP對一個動作進行封裝,則有點不倫不類。
換而言之,OOD/OOP面向名詞領域,AOP面向動詞領域。
關係
很多人在初次接觸 AOP 的時候可能會說,AOP 能做到的,一個定義良好的 OOP 的接口也一樣能夠做到,我想這個觀點是值得商榷的。AOP和定義良好的 OOP 的接口可以說都是用來解決並且實現需求中的橫切問題的方法。但是對於 OOP 中的接口來說,它仍然需要我們在相應的模組中去調用該接口中相關的方法,這是 OOP 所無法避免的,並且一旦接口不得不進行修改的時候,所有事情會變得一團糟;AOP 則不會這樣,你只需要修改相應的 Aspect,再重新編織(weave)即可。 當然,AOP 也絕對不會代替 OOP。核心的需求仍然會由 OOP 來加以實現,而 AOP 將會和 OOP 整合起來,以此之長,補彼之短。
套用舉例
假設在一個套用系統中,有一個共享的數據必須被並發同時訪問,首先,將這個數據封裝在數據對象中,稱為Data Class,同時,將有多個訪問類,專門用於在同一時刻訪問這同一個數據對象。
為了完成上述並發訪問同一資源的功能,需要引入鎖Lock的概念,也就是說,某個時刻,當有一個訪問類訪問這個數據對象時,這個數據對象必須上鎖Locked,用完後就立即解鎖unLocked,再供其它訪問類訪問。
使用傳統的編程習慣,我們會創建一個抽象類,所有的訪問類繼承這個抽象父類,如下:
accessDataObject()方法需要有“鎖”狀態之類的相關代碼。
Java只提供了單繼承,因此具體訪問類只能繼承這個父類,如果具體訪問類還要繼承其它父類,比如另外一個如Worker的父類,將無法方便實現。
重用被打折扣,具體訪問類因為也包含“鎖”狀態之類的相關代碼,只能被重用在相關有“鎖”的場合,重用範圍很窄。
仔細研究這個套用的“鎖”,它其實有下列特性:
“鎖”功能不是具體訪問類的首要或主要功能,訪問類主要功能是訪問數據對象,例如讀取數據或更改動作。
“鎖”
“鎖”功能其實是這個系統的一個縱向切面,涉及許多類、許多類的方法。如右圖:
因此,一個新的程式結構應該是關注系統的縱向切面,例如這個套用的“鎖”功能,這個新的程式結構就是aspect(方面)
在這個套用中,“鎖”方面(aspect)應該有以下職責:
提供一些必備的功能,對被訪問對象實現加鎖或解鎖功能。以保證所有在修改數據對象的操作之前能夠調用lock()加鎖,在它使用完成後,調用unlock()解鎖。
套用範圍
很明顯,AOP非常適合開發J2EE容器伺服器,JBoss 4.0正是使用AOP框架進行開發。
具體功能如下:
Authentication 許可權
Caching快取
Context passing內容傳遞
Error handling 錯誤處理
Lazy loading 延時載入
Debugging 調試
logging, tracing, profiling and monitoring 記錄跟蹤 最佳化 校準
Performance optimization性能最佳化
Persistence 持久化
Resource pooling資源池
Synchronization 同步
Transactions事務
【AOP有必要嗎?】
當然,上述套用範例在沒有使用AOP情況下,也得到了解決,例如JBoss 3.XXX也提供了上述套用功能,並且沒有使用AOP。
但是,使用AOP可以讓我們從一個更高的抽象概念來理解軟體系統,AOP也許提供一種有價值的工具。可以這么說:因為使用AOP結構,JBoss 4.0的源碼要比JBoss 3.X容易理解多了,這對於一個大型複雜系統來說是非常重要的。
從另外一個方面說,好像不是所有的人都需要關心AOP,它可能是一種架構設計的選擇,如果選擇J2EE系統,AOP關注的上述通用方面都已經被J2EE容器實現了,J2EE套用系統開發者可能需要更多地關注行業套用方面aspect。
傳統的程式通常表現出一些不能自然地適合單一的程式模組或者是幾個緊密相關的程式模組的行為,AOP 將這種行為稱為橫切,它們跨越了給定編程模型中的典型職責界限。橫切行為的實現都是分散的,軟體設計師會發現這種行為難以用正常的邏輯來思考、實現和更改。最常見的一些橫切行為如下面這些:
日誌記錄,跟蹤,最佳化和監控
事務的處理
持久化
性能的最佳化
資源池,如資料庫連線池的管理
系統統一的認證、許可權管理等
套用系統的異常捕捉及處理
針對具體行業套用的橫切行為
前面幾種橫切行為都已經得到了密切的關注,也出現了各種有價值的套用,但也許今後幾年,AOP 對針對具體行業套用的貢獻會成為令人關注的焦點。
實現項目
AOP是一個概念,並沒有設定具體語言的實現,它能克服那些只有單繼承特性語言的缺點(如Java),AOP具體實現有以下幾個項目:
AspectJ (TM): 創建於Xerox PARC. 有近十年歷史,成熟
缺點:過於複雜;破壞封裝;需要專門的Java編譯器。
動態AOP:使用JDK的動態代理API或位元組碼Bytecode處理技術。
基於動態代理API的具體項目有:
JBoss 4.0 JBoss 4.0伺服器
基於位元組碼的項目有:
aspectwerkz ,spring
作用
面向過程編程離我們已經有些遙遠,面向對象編程正主宰著軟體世界。當每個新的軟體設計師都被要求掌握如何將需求功能轉化成一個個類,並且定義它們的數據成員、行為,以及它們之間複雜的關係的時候,面向切面編程(Aspect-Oriented Programming,AOP)為我們帶來了新的想法、新的思想、新的模式。
如果說面向對象編程是關注將需求功能劃分為不同的並且相對獨立,封裝良好的類,並讓它們有著屬於自己的行為,依靠繼承和多態等來定義彼此的關係的話;那么面向切面編程則是希望能夠將通用需求功能從不相關的類當中分離出來,能夠使得很多類共享一個行為,一旦發生變化,不必修改很多類,而只需要修改這個行為即可。
面向切面編程是一個令人興奮不已的新模式。就開發軟體系統而言,它的影響力必將會和有著數十年套用歷史的面向對象編程一樣巨大。面向切面編程和面向對象編程不但不是互相競爭的技術而且彼此還是很好的互補。面向對象編程主要用於為同一對象層次的公用行為建模。它的弱點是將公共行為套用於多個無關對象模型之間。而這恰恰是面向切面編程適合的地方。有了 AOP,我們可以定義交叉的關係,並將這些關係套用於跨模組的、彼此不同的對象模型。AOP 同時還可以讓我們層次化功能性而不是嵌入功能性,從而使得代碼有更好的可讀性和易於維護。它會和面向對象編程合作得很好。
實現
AOP 是一個概念,一個規範,本身並沒有設定具體語言的實現,這實際上提供了非常廣闊的發展的空間。AspectJ是AOP的一個很悠久的實現,它能夠和 Java 配合起來使用。
介紹 AspectJ 的使用和編碼不是本文的目的,你可以在 Google 上找到很多有關它的材料。
這裡只是重溫 AspectJ 中幾個必須要了解的概念:
Aspect: Aspect 聲明類似於 Java 中的類聲明,在 Aspect 中會包含著一些 Pointcut 以及相應的 Advice。
Joint point:表示在程式中明確定義的點,典型的包括方法調用,對類成員的訪問以及異常處理程式塊的執行等等,它自身還可以嵌套其它 joint point。
Pointcut:表示一組 joint point,這些 joint point 或是通過邏輯關係組合起來,或是通過通配、正則表達式等方式集中起來,它定義了相應的 Advice 將要發生的地方。
Advice:Advice 定義了在 pointcut 裡面定義的程式點具體要做的操作,它通過 before、after 和 around 來區別是在每個 joint point 之前、之後還是代替執行的代碼。
下面要討論的這些問題,也許正是接觸了 AOP 之後所困惑的。
AOP 幫助我們解決了新的問題沒有?
AOP 並沒有幫助我們解決任何新的問題,它只是提供了一種更好的辦法,能夠用更少的工作量來解決現有的一些問題,並且使得系統更加健壯,可維護性更好。同時,它讓我們在進行系統架構和模組設計的時候多了新的選擇和新的思路。
工業化套用
這個問題很難回答,其實最好的答案就是嘗試,用成功的項目或是產品來回答。Jboss 4.0 就是完全採用 AOP 的思想來設計的 EJB 容器,它已經通過了 J2EE 的認證,並且在工業化套用中證明是一個優秀的產品。相信在不遠的將來,會出現更多採用 AOP 思想設計的產品和行業套用。
小結
AOP 正向我們走來,我們需要關注的是怎么樣使得它能夠為我們的軟體系統的設計和實現帶來幫助。本文旨在給大家一點啟發,能夠在更多的領域更深入的套用 AOP 的思想。