據統計,目前與AOP相關的專案已達近百種,而基於Java的AOP實現機制也有二十多種,以下所列舉的是商業上得到成熟應用的幾種基於Java的AOP的實現機制。
1.AspectJ
AspectJ是目前最完善的AOP語言,由AOP的首倡者Gregor Kiczales領導的一個小組提出並得到發展。AspectJ是對Java程式語言的擴充套件,通過增加了一些新的構造塊支援對橫切關注點的模組化封裝,通過對原始碼級別的程式碼混合實現織入,是一種典型的使用靜態織入的AOP實現機制。AspectJ提供了兩種橫切實現機制,一種稱為動態橫切(Dynamic Crosscutting),另一種稱為靜態橫切(Static Crosscutting)。
動態橫切是指在程式執行的某一個明確的點上執行額外的,預先定義好的實現,是一種靜態實現機制,並非是動態的。為了實現動態橫切,AspectJ中引入了四個新的概念:連線點(Join Point),切入點(Pointcut),參考(Advice)和方面(Aspect)。連線點是明確定義的程式執行過程中的一個點,切入點則是指一組相關的連線點,參考定義了在連線點執行的額外實現,方面則是指對橫切關注點的模組化封裝實現的單元,類似於AOP中的類,由切入點,參考與普通的Java成員宣告組成。如前所述,連線點是程式執行中明確定義的點,比如,類接受到方法呼叫時,方法呼叫時,屬性訪問時都是連線點的例子,在連線點處可以執行預定義的額外實現。而要指明在哪些連線點上執行,則需要定義切入點,切入點可以在程式執行時匹配特定的連線點,AspectJ中預定義了一系列標準切入點,包括方法與構造器的呼叫,接受呼叫,執行,域的get,set訪問,異常處理,例項型別匹配,處於類或方法體中,控制流中,呼叫者呼叫方法,型別的初始化與靜態初始化,通過這些預定義切入點的組合可以實現自定義的、複雜的切入點。在編譯時,方面中的參考將被轉化為標準的方法,類程式碼中匹配切入點的連線點將被轉化為一個靜態的標記點,然後,這些靜態的點將被對參考所轉化成的方法的呼叫所取代,由此完成兩種程式碼的織入,最後對織入完成的程式碼編譯為位元組碼,即完成了整個編譯過程。目前,AspectJ即支援編譯前的預處理方式實現程式碼的織入,也支援編譯後的位元組碼操作。
靜態橫切是指對已存在的型別定義引入新的方法,屬性等,與動態橫切不同,靜態橫切不改變型別的動態行為,而是改變其靜態結構,也即匯入(Introduction)。通過在方面程式碼中宣告方法,屬性,需要繼承的超類,介面等,在程式碼織入時,可以改變應用此方面的類的定義。
2.AspectWerkz
AspectWerkz是一個動態的AOP框架,利用對位元組碼的修改實現方面的織入,並使用Java虛擬機器的動態替換位元組碼的能力實現動態AOP的要求。AspectWerkz沒有擴充套件Java語言,方面、參考、切入點等均使用標準的Java構造塊,即類以及方法來實現,並使用XML檔案定義這些構造塊,此外AspectWerkz還支援使用JavaDoc標記實現的執行期屬性定義。AspectWerkz採用了與AspectJ相似的連線點模型,只是支援的連線點種類少於AspectJ,參考的型別一致。
AspectWerkz通過引入一個間接層,方面容器(Aspect Container)以及對位元組碼的轉化,即程式碼織入實現動態AOP的要求,方面容器管理部署好的類、方面程式碼,並根據XML檔案或JavaDoc註釋中定義的方面,參考,切入點等得到連線點處相關的方面資訊,並在程式的執行中控制執行流程,在匹配的連線點處執行適當的參考。
AspectWerkz通過類載入層次的適當位置攔截類載入從而實現位元組碼的修飾。AspectWerkz提供了兩種織入模式實現AOP:靜態織入以及動態織入。靜態織入只在類載入時對位元組碼作一次性的轉化,通過將類的方法實現移入AspectWerkz命名的方法中,將原方法中的程式碼改寫,由方面容器呼叫適當的參考,並呼叫前述AspectWerkz新增的方法從而完成程式碼的織入。匯入則由混合型別(Mixin)實現,用於為類增加新的方法,混合型別是一種使用介面與實現類的方式模擬多重繼承的機制。AspectWerkz通過修改位元組碼使被匯入的類實現混合型別的介面,並在介面定義的方法中,將控制交給容器管理器,由它來完成對實現的呼叫。靜態織入可以在執行時動態的為切入點增加,刪除參考,可以引入新的參考,但是無法定義新的切入點,這需要動態織入。動態織入由兩階段織入完成,分別為類載入階段與啟用階段。首先,在類載入時,按照靜態織入的方法,為需要實現動態織入的類的每個方法新增一個相應的空的方法,匹配連線點的方法除外。然後,在啟用階段,原方法體中的程式碼將被交換到類載入時新產生的方法中,原方法將實現靜態織入時相同的處理,從而方面容器控制流程。前述程式碼交換是由熱交換(HotSwap)實現的,這是JVM提供的API。通過方面容器與織入模型,AspectWerkz提供了動態AOP的實現。
3.SpringFramework
SpringFramework是一個採用了反轉控制(Inversion of Control, IoC)策略的基於J2EE的輕量級應用框架。SpringFramework的核心是IoC容器,對於其它應用,如資料庫訪問,日誌等,SpringFramework多使用現有的、成熟的框架。SpringFramework採用了模組化的方式,各模組可以共同使用,也可以單獨使用其中的一個模組, SpringFramework的一個模組提供了對動態AOP的支援,SpringFramework中提供的宣告式事務管理就是基於動態AOP的。
SpringFramework中AOP的實現基於動態代理(Dynamic Proxy), 動態代理源於代理模式,即通過介面實現對業務物件的訪問,但動態代理無需為每一個需代理的業務物件靜態的生成代理物件,只需提供需要代理的介面與代理實現,就可以在執行時動態的生成代理物件,代理對上述介面的訪問,同樣的機制也使用於對類的代理,使用類似於修飾者的模式,通過子類化實現。SpringFramework預設使用JDK提供的動態代理機制,此時,業務物件通過介面程式設計,若需要代理對類的訪問,則需要使用CGLIB,這是一個開源的動態代理實現。
SpringFramework的AOP實現不同於AspectJ與AspectWerkz,它不是完全的AOP實現,而是設計用於在應用伺服器環境下實現AOP,與SpringFramework的IoC容器配合使用。SpringFramework中參考,切入點與方面均由普通Java物件實現,其中連線點模型與AspectJ相同,只是遠不如AspectJ豐富,目前只提供對方法呼叫的攔截。有4種型別的參考,分別為方法呼叫時,之前,返回時與丟擲異常時,通過實現SpringFramework的參考介面可以自定義參考型別。在SpringFramework中,方面稱為Advisor,是一個包含參考與切入點的Java類。像其它由IoC容器管理的元件一樣,參考,切入點與方面也由IoC容器管理 ,由XML配置檔案定義。配置的內容包括業務物件的介面與實現,自定義的或由SpringFramework提供的切入點與參考類,或使用Adviser類取代單獨的切入點與參考類。在執行時,通過IoC容器進行名稱查詢,就可以由容器使用代理機制自動產生代理物件,並在符合切入點定義的連線點處執行參考。SpringFramework除自身實現的AOP框架外,還在尋求與其它AOP實現機制的整合,目前已經實現了與AspectJ的整合,以利用AspectJ豐富的切入點語法,並利用AspectJ的方面實現。
4.JBoss
JBoss是一個開源的符合J2EE規範的應用伺服器,作為J2EE規範的補充,Jboss中引入了AOP框架,為普通Java類提供了J2EE服務,而無需遵循EJB規範。Jboss通過類載入時,使用Javassist對位元組碼操作實現動態AOP框架,Javassist是一個開源的編輯位元組碼的類庫。
Jboss中參考,切入點與方面也由普通Java物件實現,並使用XML檔案配置。Jboss的連線點模型與AspectJ略有不同,提供了一系列預定義的切入點,包括類匹配,方法呼叫,構造器呼叫,域訪問,特定的呼叫與被呼叫關係。通過這些切入點的邏輯運算,可以實現更為複雜的切入點。方面為Java類,參考是其中的一個方法,方面中不含切入點,方面主要為各種攔截器(Interceptor),攔截器即為只含一個參考的方面,單一連線點上可由多個攔截器形成攔截器鏈,攔截器執行額外的操作。對方法的攔截由Advisor類管理,在連線點依次呼叫攔截器,並最終呼叫被邏輯的方法。而關於切入點,參考已及方面的資訊由AspectManager管理。此外,Jboss提供對後設資料的支援,用於為類,方法,構造器以及域新增額外的屬性,並可在執行期訪問。
為實現攔截,Jboss需要修改類的位元組碼,大致過程如下。XML配置檔案中關於切入點,攔截器,後設資料以及混合類的資訊在應用程式部署時被讀入、解析,並生成相應的物件,這些資訊與例項化的物件由AspectManager管理。在需要混入方面程式碼的類載入時,AspectManager將建立Advisor類,將方面相關資訊傳遞給它,並對類的位元組碼進行修改,之後將修改過的位元組碼交給類載入器完成類的裝載。位元組碼的修改主要是對被載入的類新增一系列方法用於代理那些匹配連線點的方法呼叫,構造器呼叫,域訪問以及方法匯入,轉為對Advisor類相應方法的呼叫。類中各方法將重新命名,保留原方法體,並新增一個與原方法同名的方法,在這個方法中呼叫那些代理方法,用來將呼叫代理給Advisor類,或呼叫重新命名的原方法。對於域訪問,分別新增兩個方法,對應於讀與寫操作,將域訪問代理至Advisor類,在訪問這個域的類中,則需將對域的訪問轉換為對上述方法的呼叫。對於構造器呼叫,則新增一個方法,將呼叫代理至Advisor類,並對構造物件的類的構造程式碼作相應轉換。對於匯入,被匯入的類中將新增一個混合類實現的引用,並新增混合類介面中的方法,將對混合類方法的呼叫代理至Advisor類,並最終呼叫混合類的實現。相關類載入後,初始化Advisor類,填入攔截器鏈,以完成整個處理過程。 |