據統計,目前與AOP相關(guān)的項目已達近百種,而基于Java的AOP實(shí)現機制也有二十多種,以下所列舉的是商業(yè)上得到成熟應用的幾種基于Java的AOP的實(shí)現機制。
AspectJ
AspectJ是目前最完善的AOP語(yǔ)言,由AOP的首倡者Gregor Kiczales領(lǐng)導的一個(gè)小組提出并得到發(fā)展。AspectJ是對Java編程語(yǔ)言的擴展,通過(guò)增加了一些新的構造塊支持對橫切關(guān)注點(diǎn)的模塊化封裝,通過(guò)對源代碼級別的代碼混合實(shí)現織入,是一種典型的使用靜態(tài)織入的AOP實(shí)現機制。AspectJ提供了兩種橫切實(shí)現機制,一種稱(chēng)為動(dòng)態(tài)橫切(Dynamic Crosscutting),另一種稱(chēng)為靜態(tài)橫切(Static Crosscutting)。
動(dòng)態(tài)橫切是指在程序執行的某一個(gè)明確的點(diǎn)上運行額外的,預先定義好的實(shí)現,是一種靜態(tài)實(shí)現機制,并非是動(dòng)態(tài)的。為了實(shí)現動(dòng)態(tài)橫切,AspectJ中引入了四個(gè)新的概念:連接點(diǎn)(Join Point),切入點(diǎn)(Pointcut),參考(Advice)和方面(Aspect)。連接點(diǎn)是明確定義的程序執行過(guò)程中的一個(gè)點(diǎn),切入點(diǎn)則是指一組相關(guān)的連接點(diǎn),參考定義了在連接點(diǎn)執行的額外實(shí)現,方面則是指對橫切關(guān)注點(diǎn)的模塊化封裝實(shí)現的單元,類(lèi)似于AOP中的類(lèi),由切入點(diǎn),參考與普通的Java成員聲明組成。如前所述,連接點(diǎn)是程序執行中明確定義的點(diǎn),比如,類(lèi)接受到方法調用時(shí),方法調用時(shí),屬性訪(fǎng)問(wèn)時(shí)都是連接點(diǎn)的例子,在連接點(diǎn)處可以執行預定義的額外實(shí)現。而要指明在哪些連接點(diǎn)上執行,則需要定義切入點(diǎn),切入點(diǎn)可以在程序運行時(shí)匹配特定的連接點(diǎn),AspectJ中預定義了一系列標準切入點(diǎn),包括方法與構造器的調用,接受調用,執行,域的get,set訪(fǎng)問(wèn),異常處理,實(shí)例類(lèi)型匹配,處于類(lèi)或方法體中,控制流中,調用者調用方法,類(lèi)型的初始化與靜態(tài)初始化,通過(guò)這些預定義切入點(diǎn)的組合可以實(shí)現自定義的、復雜的切入點(diǎn)。在編譯時(shí),方面中的參考將被轉化為標準的方法,類(lèi)代碼中匹配切入點(diǎn)的連接點(diǎn)將被轉化為一個(gè)靜態(tài)的標記點(diǎn),然后,這些靜態(tài)的點(diǎn)將被對參考所轉化成的方法的調用所取代,由此完成兩種代碼的織入,最后對織入完成的代碼編譯為字節碼,即完成了整個(gè)編譯過(guò)程。目前,AspectJ即支持編譯前的預處理方式實(shí)現代碼的織入,也支持編譯后的字節碼操作。
靜態(tài)橫切是指對已存在的類(lèi)型定義引入新的方法,屬性等,與動(dòng)態(tài)橫切不同,靜態(tài)橫切不改變類(lèi)型的動(dòng)態(tài)行為,而是改變其靜態(tài)結構,也即導入(Introduction)。通過(guò)在方面代碼中聲明方法,屬性,需要繼承的超類(lèi),接口等,在代碼織入時(shí),可以改變應用此方面的類(lèi)的定義。
AspectWerkz
AspectWerkz是一個(gè)動(dòng)態(tài)的AOP框架,利用對字節碼的修改實(shí)現方面的織入,并使用Java虛擬機的動(dòng)態(tài)替換字節碼的能力實(shí)現動(dòng)態(tài)AOP的要求。AspectWerkz沒(méi)有擴展Java語(yǔ)言,方面、參考、切入點(diǎn)等均使用標準的Java構造塊,即類(lèi)以及方法來(lái)實(shí)現,并使用XML文件定義這些構造塊,此外AspectWerkz還支持使用JavaDoc標記實(shí)現的運行期屬性定義。AspectWerkz采用了與AspectJ相似的連接點(diǎn)模型,只是支持的連接點(diǎn)種類(lèi)少于AspectJ,參考的類(lèi)型一致。
AspectWerkz通過(guò)引入一個(gè)間接層,方面容器(Aspect Container)以及對字節碼的轉化,即代碼織入實(shí)現動(dòng)態(tài)AOP的要求,方面容器管理部署好的類(lèi)、方面代碼,并根據XML文件或JavaDoc注釋中定義的方面,參考,切入點(diǎn)等得到連接點(diǎn)處相關(guān)的方面信息,并在程序的執行中控制執行流程,在匹配的連接點(diǎn)處執行適當的參考。
AspectWerkz通過(guò)類(lèi)載入層次的適當位置攔截類(lèi)載入從而實(shí)現字節碼的修飾。AspectWerkz提供了兩種織入模式實(shí)現AOP:靜態(tài)織入以及動(dòng)態(tài)織入。靜態(tài)織入只在類(lèi)載入時(shí)對字節碼作一次性的轉化,通過(guò)將類(lèi)的方法實(shí)現移入AspectWerkz命名的方法中,將原方法中的代碼改寫(xiě),由方面容器調用適當的參考,并調用前述AspectWerkz添加的方法從而完成代碼的織入。導入則由混合類(lèi)型(Mixin)實(shí)現,用于為類(lèi)增加新的方法,混合類(lèi)型是一種使用接口與實(shí)現類(lèi)的方式模擬多重繼承的機制。AspectWerkz通過(guò)修改字節碼使被導入的類(lèi)實(shí)現混合類(lèi)型的接口,并在接口定義的方法中,將控制交給容器管理器,由它來(lái)完成對實(shí)現的調用。靜態(tài)織入可以在運行時(shí)動(dòng)態(tài)的為切入點(diǎn)增加,刪除參考,可以引入新的參考,但是無(wú)法定義新的切入點(diǎn),這需要動(dòng)態(tài)織入。動(dòng)態(tài)織入由兩階段織入完成,分別為類(lèi)載入階段與激活階段。首先,在類(lèi)載入時(shí),按照靜態(tài)織入的方法,為需要實(shí)現動(dòng)態(tài)織入的類(lèi)的每個(gè)方法添加一個(gè)相應的空的方法,匹配連接點(diǎn)的方法除外。然后,在激活階段,原方法體中的代碼將被交換到類(lèi)載入時(shí)新產(chǎn)生的方法中,原方法將實(shí)現靜態(tài)織入時(shí)相同的處理,從而方面容器控制流程。前述代碼交換是由熱交換(HotSwap)實(shí)現的,這是JVM提供的API。通過(guò)方面容器與織入模型,AspectWerkz提供了動(dòng)態(tài)AOP的實(shí)現。
SpringFramework
SpringFramework是一個(gè)采用了反轉控制(Inversion of Control, IoC)策略的基于J2EE的輕量級應用框架。SpringFramework的核心是IoC容器,對于其它應用,如數據庫訪(fǎng)問(wèn),日志等,SpringFramework多使用現有的、成熟的框架。SpringFramework采用了模塊化的方式,各模塊可以共同使用,也可以單獨使用其中的一個(gè)模塊, SpringFramework的一個(gè)模塊提供了對動(dòng)態(tài)AOP的支持,SpringFramework中提供的聲明式事務(wù)管理就是基于動(dòng)態(tài)AOP的。
SpringFramework中AOP的實(shí)現基于動(dòng)態(tài)代理(Dynamic Proxy), 動(dòng)態(tài)代理源于代理模式,即通過(guò)接口實(shí)現對業(yè)務(wù)對象的訪(fǎng)問(wèn),但動(dòng)態(tài)代理無(wú)需為每一個(gè)需代理的業(yè)務(wù)對象靜態(tài)的生成代理對象,只需提供需要代理的接口與代理實(shí)現,就可以在運行時(shí)動(dòng)態(tài)的生成代理對象,代理對上述接口的訪(fǎng)問(wèn),同樣的機制也使用于對類(lèi)的代理,使用類(lèi)似于修飾者的模式,通過(guò)子類(lèi)化實(shí)現。SpringFramework默認使用JDK提供的動(dòng)態(tài)代理機制,此時(shí),業(yè)務(wù)對象通過(guò)接口編程,若需要代理對類(lèi)的訪(fǎng)問(wèn),則需要使用CGLIB,這是一個(gè)開(kāi)源的動(dòng)態(tài)代理實(shí)現。
SpringFramework的AOP實(shí)現不同于AspectJ與AspectWerkz,它不是完全的AOP實(shí)現,而是設計用于在應用服務(wù)器環(huán)境下實(shí)現AOP,與SpringFramework的IoC容器配合使用。SpringFramework中參考,切入點(diǎn)與方面均由普通Java對象實(shí)現,其中連接點(diǎn)模型與AspectJ相同,只是遠不如AspectJ豐富,目前只提供對方法調用的攔截。有4種類(lèi)型的參考,分別為方法調用時(shí),之前,返回時(shí)與拋出異常時(shí),通過(guò)實(shí)現SpringFramework的參考接口可以自定義參考類(lèi)型。在SpringFramework中,方面稱(chēng)為Advisor,是一個(gè)包含參考與切入點(diǎn)的Java類(lèi)。像其它由IoC容器管理的組件一樣,參考,切入點(diǎn)與方面也由IoC容器管理 ,由XML配置文件定義。配置的內容包括業(yè)務(wù)對象的接口與實(shí)現,自定義的或由SpringFramework提供的切入點(diǎn)與參考類(lèi),或使用Adviser類(lèi)取代單獨的切入點(diǎn)與參考類(lèi)。在運行時(shí),通過(guò)IoC容器進(jìn)行名稱(chēng)查找,就可以由容器使用代理機制自動(dòng)產(chǎn)生代理對象,并在符合切入點(diǎn)定義的連接點(diǎn)處執行參考。SpringFramework除自身實(shí)現的AOP框架外,還在尋求與其它AOP實(shí)現機制的整合,目前已經(jīng)實(shí)現了與AspectJ的整合,以利用AspectJ豐富的切入點(diǎn)語(yǔ)法,并利用AspectJ的方面實(shí)現。
JBoss
JBoss是一個(gè)開(kāi)源的符合J2EE規范的應用服務(wù)器,作為J2EE規范的補充,Jboss中引入了AOP框架,為普通Java類(lèi)提供了J2EE服務(wù),而無(wú)需遵循EJB規范。Jboss通過(guò)類(lèi)載入時(shí),使用Javassist對字節碼操作實(shí)現動(dòng)態(tài)AOP框架,Javassist是一個(gè)開(kāi)源的編輯字節碼的類(lèi)庫。
Jboss中參考,切入點(diǎn)與方面也由普通Java對象實(shí)現,并使用XML文件配置。Jboss的連接點(diǎn)模型與AspectJ略有不同,提供了一系列預定義的切入點(diǎn),包括類(lèi)匹配,方法調用,構造器調用,域訪(fǎng)問(wèn),特定的調用與被調用關(guān)系。通過(guò)這些切入點(diǎn)的邏輯運算,可以實(shí)現更為復雜的切入點(diǎn)。方面為Java類(lèi),參考是其中的一個(gè)方法,方面中不含切入點(diǎn),方面主要為各種攔截器(Interceptor),攔截器即為只含一個(gè)參考的方面,單一連接點(diǎn)上可由多個(gè)攔截器形成攔截器鏈,攔截器執行額外的操作。對方法的攔截由Advisor類(lèi)管理,在連接點(diǎn)依次調用攔截器,并最終調用被邏輯的方法。而關(guān)于切入點(diǎn),參考已及方面的信息由AspectManager管理。此外,Jboss提供對元數據的支持,用于為類(lèi),方法,構造器以及域添加額外的屬性,并可在運行期訪(fǎng)問(wèn)。
為實(shí)現攔截,Jboss需要修改類(lèi)的字節碼,大致過(guò)程如下。XML配置文件中關(guān)于切入點(diǎn),攔截器,元數據以及混合類(lèi)的信息在應用程序部署時(shí)被讀入、解析,并生成相應的對象,這些信息與實(shí)例化的對象由AspectManager管理。在需要混入方面代碼的類(lèi)載入時(shí),AspectManager將創(chuàng )建Advisor類(lèi),將方面相關(guān)信息傳遞給它,并對類(lèi)的字節碼進(jìn)行修改,之后將修改過(guò)的字節碼交給類(lèi)載入器完成類(lèi)的裝載。字節碼的修改主要是對被載入的類(lèi)添加一系列方法用于代理那些匹配連接點(diǎn)的方法調用,構造器調用,域訪(fǎng)問(wèn)以及方法導入,轉為對Advisor類(lèi)相應方法的調用。類(lèi)中各方法將重命名,保留原方法體,并添加一個(gè)與原方法同名的方法,在這個(gè)方法中調用那些代理方法,用來(lái)將調用代理給Advisor類(lèi),或調用重命名的原方法。對于域訪(fǎng)問(wèn),分別添加兩個(gè)方法,對應于讀與寫(xiě)操作,將域訪(fǎng)問(wèn)代理至Advisor類(lèi),在訪(fǎng)問(wèn)這個(gè)域的類(lèi)中,則需將對域的訪(fǎng)問(wèn)轉換為對上述方法的調用。對于構造器調用,則添加一個(gè)方法,將調用代理至Advisor類(lèi),并對構造對象的類(lèi)的構造代碼作相應轉換。對于導入,被導入的類(lèi)中將添加一個(gè)混合類(lèi)實(shí)現的引用,并添加混合類(lèi)接口中的方法,將對混合類(lèi)方法的調用代理至Advisor類(lèi),并最終調用混合類(lèi)的實(shí)現。相關(guān)類(lèi)載入后,初始化Advisor類(lèi),填入攔截器鏈,以完成整個(gè)處理過(guò)程。