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