1. 程式人生 > >Java平臺AOP技術研究

Java平臺AOP技術研究

3.1 Java平臺AOP技術概覽 3.1.1 AOP技術在Java平臺中的應用 AOP在實驗室應用和商業應用上,Java平臺始終走在前面。從最初也是目前最成熟的AOP工具——AspectJ,到目前已經融和在企業級容器JBoss中的JBoss AOP,均建立在Java平臺上。 前面已經描述到,AOP的目的就是將核心關注點和橫切關注點分離,實際上這就是一種分散關注(seperation of concerns)的思路。在Java平臺下,如果要開發企業級的應用,非J2EE莫屬。一個J2EE應用系統只有部署在J2EE容器中才能執行,那麼為什麼要劃分為J2EE容器和J2EE應用系統? 通過對J2EE容器執行機制的分析,我們發現:實際上J2EE容器分離了一般應用系統的一些通用功能,例如事務機制、安全機制以及物件池或執行緒池等效能優化機制。這些功能機制是每個應用系統幾乎都需要的,因此可以從具體應用系統中分離出來,形成一個通用的框架平臺,而且,這些功能機制的設計開發有一定難度,同時執行的穩定性和快速性都非常重要,必須經過長時間除錯和執行經驗積累而成,因此,形成了專門的J2EE容器伺服器產品,如Tomcat JBoss、Websphere、WebLogic等。 從J2EE將應用系統和容器分離的策略,我們能夠看到AOP的影子。J2EE應用系統就相當於AOP技術械暮誦墓刈⒌悖哪諶葜饕ㄆ笠迪低車納桃德嘸歡鳭2EE容器則類似於橫切關注點,實現的是通用的功能機制。不過業界在選擇J2EE容器時,對於EJB這種重量級容器伺服器而言,雖然欣賞其高效、穩定及企業級的容器服務,但對於整個容器的高開銷、高成本以及過於複雜的解決方案均深懷戒心。因此,隨著J2EE的逐步演化,“輕量級容器架構”通過開源社群如激流一般的驅動力,逐漸佔據了J2EE技術的強勢地位。而所謂“輕量級容器”與EJB提供的重量級架構的區別,就在於藉助了AOP技術和IoC(Inversion of Control,反轉模式)機制,降低了程式碼對於專用介面的依賴性,以簡短、輕便、專注、可移植的方式實現業務物件。事實上,我們看到的美好前景是,如果所有企業級服務都可以通過AOP機制提供給普通Java物件,那麼深盔重鎧的應用伺服器就不再有存在的價值了。 正是看到了AOP技術在企業級開發中的巨大潛力,而“輕量級容器”也喚起了改革EJB容器的呼聲(事實上,最新的 EJB V3.0 標準就使用了輕量級容器模型),越來越多的AOP工具在Java平臺下應運而生,從而形成了目前AOP工具百家爭鳴的局面。其中,應用最為廣泛的主要包括AspectJ、Spring AOP和JBoss AOP等。 3.1.2 Java平臺下AOP工具的比較 AOP是一項新技術,而在Java平臺下實現該技術的工具也非常多。雖然AOP的技術要素從本質上來講是一致的,但各種工具的實現方法也各有不同,本節基於AOP的技術要素,對當前應用較廣泛的AspectJ、Spring AOP和JBoss AOP進行比較。 3.1.2.1 AOP實現機制的區別 同樣是實現AOP,且AOP的技術要素完全相同,但各種AOP工具對於AOP實現的底層機制卻是不盡相同的。 AspectJ採用了原始碼生成技術來實現AOP。它提供了一套獨有的基於Java平臺的AOP語法,以及專有的AspectJ編譯器。編譯器在編譯具有AspectJ語法的Java程式時,能夠識別諸如aspect,pointcut等特殊關鍵字,然後利用靜態織入的方式,修改需要被擷取的方法所屬類的原始碼,把advice或者introduce的業務邏輯程式碼注入到正確的位置。利用AspectJ,可以將核心關注點完全獨立出來,然後通過AspectJ語法,編寫符合核心關注點要求的橫切關注點程式碼,最後通過AspectJ編譯器,將這兩者在後期結合起來。採用這種靜態織入技術,使得運用了AOP技術的系統在執行效能上未受到任何損失,因為它沒有利用反射技術或代理技術,而僅僅是程式的靜態擴充套件而已。然而這種原始碼生成方式實現的AOP雖然在效能上具備一定的優勢,但它同時會給開發帶來一定的問題。例如程式碼的後期修改會給系統帶來不可估量的影響。 Spring AOP是Spring框架中的一部分,但可以作為一個獨立的模組單獨存在。Spring AOP實現AOP技術從本質上來講,是利用了JDK提供的動態代理技術。而從實際的實現方式來看,則是利用了IoC(Inversion of Control,反轉模式)機制,同時採用了AOP聯盟(AOP Alliance)的通用AOP介面。首先,Spring AOP通過xml配置檔案配置了pointcut,並利用Interceptor(攔截機)作為設定的觸發條件。Interceptor是由使用者自定義的,它相當於是AOP中的advice,但該Interceptor需要實現AOP聯盟的通用AOP介面,例如org.aopalliance.intercept.MethodInterceptor。最後定義一個Spring AOP ProxyFactory用於載入執行AOP元件,並利用IoC機制將advice注入到介面以及實現類中。 JBoss 4.0提供了AOP框架。與Spring一樣,這個框架可與JBoss應用伺服器緊密結合,也可以單獨執行在自己的應用中。JBoss AOP同樣需要Interceptor攔截器來完成對方法的攔截,它要求所有的Interceptor都必須實現org.jboss.aop.Interceptor介面。在這個介面中最重要的方法就是invoke()。該方法對元資料直接進行操作,並利用反射的原理去攔截方法的訊息。Interceptor相當於AOP的advice,至於pointcut,則在xml配置檔案中配置。可以看出,Spring AOP和JBoss AOP在實現上屬於動態織入的方式,它們與AspectJ在實現上是迥然不同的兩種方式。 3.1.2.2 關於“Aspect(方面)”的區別 在對aspect的宣告上,可以使用類似Java的程式碼,註釋或xml。考慮一個常用的例子,對Account類的授權策略,如果以AOP技術來實現,運用不同的AOP工具,它們在方面宣告技術上的差異,是顯而易見的。 Aspect 中的方面宣告類似於 Java 語言中的類宣告,如圖3.1 所示。 aop3.1.gif

圖3.1 AspectJ中的方面宣告 由於 AspectJ 是 Java 語言語法和語義的擴充套件,所以它提供了自己的一套處理方面的關鍵字。除了包含欄位和方法之外,AspectJ 的方面宣告還包含pointcut和advice成員。示例中的pointcut使用了修飾符(modifier)和萬用字元(wildcard)模式來表達“所有公共方法”。對帳戶的訪問,由 pointcut 引數提供。advice使用這個引數,而pointcut則用 this(account) 把它繫結。這樣做的效果,就是捕獲了正在執行的方法所隸屬的Account物件。否則,advice的主體與方法的主體相似。advice可以包含認證程式碼,或者就像在這個示例中一樣,可以呼叫其他方法。 JBoss AOP 基於 XML 的風格來宣告方面,如圖 3.2 所示。 aop3.2.gif

圖3.2 JBoss AOP的方面宣告 在 XML 風格中,aspect、pointcut和advice的宣告都以 XML 形式表示的。advice的實現,用的是普通的 Java 方法,由JBoss AOP框架呼叫。pointcut和pointcut到advice的繫結都在方面中用XML註釋宣告。JBoss 沒有顯式地繫結 Account 引數,而是提供了對當前正在執行的物件的反射訪問,因此需要把型別轉換到對應的型別。JBoss AOP還可以通過標籤的方式對方面進行宣告。標籤均以“@”字元開始,它的使用有點類似於.Net中的Attribute。 Spring AOP同樣是基於 XML 的風格來宣告方面,如圖3.3所示。 aop3.3.gif

圖3.3 Spring AOP的方面宣告 與JBoss AOP類似,Spring的advice實現是帶有特殊引數的Java方法,由 Spring 框架呼叫。XML描述accountBean,Spring框架通過它訪問 Account 物件,包括通知使用的攔截器 advisor 及其匹配模式,還有應用到模式的向前(before) 通知。 由於Spring AOP利用了IoC機制,因此比較JBoss AOP而言,在xml配置檔案中提供了更加精細的配置。而構建、執行和配置 Spring AOP 方面的過程則與JBoss AOP基本相同,不過Spring AOP依賴的是Spring框架方便的、最小化的執行時配置,所以不需要獨立的啟動器。 3.1.2.3 語言機制的區別     由於實現機制和語法風格的不同,三種AOP工具在語言機制上也有很大的不同,以下從四個方面來描述AspectJ、JBossAOP和Spring AOP之間的區別。 (1)pointcut匹配和複合:AspectJ和 JBoss AOP 提供了類似的型別模式支援。它們都允許簽名方面的匹配,對於 Java 5 應用程式來說,這些匹配包括註釋和泛型。AspectJ提供了一種簡潔的引用多個型別的技術(例如 Account+ 表示帳戶的所有子型別)。所有的工具都支援萬用字元匹配。Spring AOP 還提供了對正則表示式的支援。雖然這看起來可能是一個強大的優勢,但還是要指出其他技術已經選擇了放棄正則表示式,好讓pointcut讀起來不是太難,同時不會存在潛在的損害。pointcut複合操作符基本上都是相同的。Spring AOP 不提供“非”操作,這個操作通常與沒有在 Spring AOP 連線點模型的容器(containment)連線點結合使用。 (2)advice形式:AspectJ 支援比其他技術更多的advice形式,而 JBoss AOP 只支援一種advice形式。每種通知形式都可以表達成 around advice,所以 JBoss 的技術是無限的,而且它確實提供了額外的簡單性。不好的一面是它損失了簡潔性。另外,強迫advice去遵守普通的 Java 規則(就像註釋和 XML 風格做的那樣),在一些情況下容易出問題,因為這些規則是為方法設計的。AspectJ 擁有把被通知方法的異常“軟化”的能力,這很有用,但是不符合方法異常檢測的標準語義。 (3)join point上下文:在 AspectJ中,通過指定和繫結pointcut引數訪問動態連線點的狀態,類似於在 Java 語言中宣告方法引數的技術(請參閱圖3.1)。這為連線點上下文提供了靜態型別化的好處。JBoss AOP 和 Spring AOP 反射性地訪問連線點的狀態,這消除了在切入點表示式中引數繫結的複雜性,代價是引數靜態型別化。Java 程式設計師習慣了方法引數靜態型別化帶來的好處,同時還可以從pointcut引數的靜態型別化得到同樣的好處。所以,在 JBoss AOP 最近的發行版本中,有提供靜態型別化的“args”的計劃。

(4)擴充套件性:aspect的擴充套件性支援庫方面的部署,這樣可以在日後為特定程式將這些庫方面具體化。例如,一個方面庫可以提供應用程式監視需要的全部邏輯和基礎設施。但是,要採用某個特定專案的庫,那麼庫使用的pointcut必須擴充套件成應用程式特定的join point。AspectJ 用抽象方面支援擴充套件性,抽象方面包含抽象的pointcut和具體的advice。擴充套件抽象方面的子方面必須具體化pointcut。JBoss AOP 使用了完全不同的技術,沒有使用抽象切入點機制。擴充套件是通過生成aspect的子類、並在 XML 中或通過註釋定義新的advice繫結而實現的。pointcut到advice的顯式繫結為JBoss AOP提供了顯著優勢,從而可以很容易地把方面擴充套件到新系統,無需要生成子類。

3.2 Java平臺下AOP主流工具研究 3.2.1 AsepctJ研究 AspectJ作為Java程式語言擴充套件的AOP工具,使得我們運用AOP技術能夠像普通的Java程式設計那樣,特殊之處,僅在於我們需要使用AspectJ提供的特殊語法。接下來,我將通過一些例項,介紹如何運用AspectJ實現AOP技術。 3.2.1.1 AspectJ語言特性 設定我們的開發專案中需要應用到日誌記錄,根據前面介紹的AOP知識,我們已經能夠從這個需求中識別出橫切關注點——日誌記錄。因此,我們需要定義關於“日誌記錄”的aspect: public aspect AutoLog {    pointcut publicMethods() : execution(public * org.apache.cactus..*(..));   pointcut logObjectCalls() :     execution(* Logger.*(..));   pointcut loggableCalls() : publicMethods() && ! logObjectCalls();   before() : loggableCalls() {     Logger.entry(thisJoinPoint.getSignature().toString());   }   after() : loggableCalls() {     Logger.exit(thisJoinPoint.getSignature().toString());   } } 如果僅僅熟悉Java程式設計,會發現有很多關鍵字是Java語言中不曾包含的,它們均是AspectJ提供的。 分析上述的程式碼,首先是aspect的宣告,它類似於Java中的類宣告,定義了一個aspect:AutoLog。在這個方面中分別包含了pointcut和advice。 pointcut共有三個:publicMethod、logObjectCalls和loggableCalls。publicMethod將選擇org.apache.cactus包中的所有公共(public)方法的執行。所謂“選擇”,就意味著它的join point為其選擇的方法。當這些方法被呼叫時,就會執行pointcut的advice程式碼。而在pointcut中,execution 是一個原始的 Pointcut(就象 int 是一種原始的 Java 型別)。它選擇與括號中定義的方法說明匹配的任何方法的執行。方法說明允許包含萬用字元。logObjectCalls的pointcut則選擇Logger 類中的所有方法的執行。第三個pointcut比較特殊,它使用&& !合併了前兩個 Pointcut,這意味著它選者了除Logger類中的公共方法以外, org.apache.cactus 中所有的公共方法。 advice在aspect中,被用來完成實際的日誌紀錄。advice有三種,分別為before、after和around。如上述程式碼中定義的advice: before() : loggableCalls() {   Logger.entry(thisJoinPoint.getSignature().toString()); } 該advice的定義表示的含義是,如果org.apache.cactus中所有的公共方法(Logger類的公共方法除外)被執行,則在這些方法執行之前,需要先執行該advice定義的邏輯。 3.2.1.2 AspectJ的高階語言特性 在本文第二部分介紹AOP技術時,提到了橫切技術的分類。其中,靜態橫切技術能夠擴充套件一個物件的結構。使用引入(Introduction),Aspect 可以向類中新增新的方法和變數、宣告一個類實現一個介面或將檢查異常轉換為未檢查異常(unchecked exception)。 3.2.1.2.1 向現有類新增變數和方法 假設您有一個表示持久儲存的資料快取的物件。為了測量資料的“更新程度”,您可能決定向該物件新增時間戳記欄位,以便容易地檢測物件是否與後備儲存器同步。由於物件表示業務資料,根據AOP的知識,我們應該將這種機制性細節從物件中隔離。使用 AspectJ,可以用如下程式碼中所顯示的語法來向現有的類新增時間戳記: public aspect Timestamp { private long ValueObject.timestamp; public long ValueObject.getTimestamp() {       return timestamp;   } public void ValueObject.timestamp() {            this.timestamp = System.currentTimeMillis();   } } 通過introduction,我們就非常方便的為ValueObject型別添加了timestamp的變數和相關方法。除了必須限定在哪個類上宣告引入的方法和成員變數以外,宣告引入的方法和成員變數幾乎與宣告常規類成員相同。 3.2.1.2.2實現多繼承功能 利用introduction,AspectJ允許向介面和類新增成員,也突破了Java語言只能單繼承的限制,允許程式按C++方式那樣實現多繼承。如果您希望上述的aspect Timestamp能夠泛化 (generalize),以便能夠對各種物件重用時間戳記程式碼,可以定義一個稱為 TimestampedObject 的介面,並使用引入(Introduction)來將相同成員和變數新增到介面而不是新增到具體類中,如下所示。 public interface TimestampedObject {     long getTimestamp();     void timestamp(); } public aspect Timestamp {     private long TimestampedObject.timestamp; public long TimestampedObject.getTimestamp() {         return timestamp;     } public void TimestampedObject.timestamp() {         this.timestamp = System.currentTimeMillis();     } } Timestamp方面由於在TimestampedObject介面中引入(introduction)了方法的實現,使得TimestampedObject介面改變其本質,成為了一個特殊的類型別。特殊之處就在於一個已經繼承了一個類的類型別,通過AspectJ的語法,仍然可以再次繼承TimestampedObject,這就間接地實現了類的多繼承。而這個特殊的AspectJ語法就是declare parents語法。declare parents和其它AspectJ 型別表達一樣,可以同時應用於多個型別: declare parents: ValueObject || BigValueObject implements TimestampedObject; 3.2.1.3 編譯器及工具支援     要讓aspect能夠正常工作,必須將aspect加入到它們要修改的程式碼中去。這項工作由AspectJ提供的ajc編譯器完成。ajc 編譯器用來編譯類和 Aspect 程式碼。ajc 既可以作為編譯器也可以作為預編譯器操作,生成有效的 .class 或 .java 檔案,可以在任何標準 Java 環境(新增一個小的執行時 JAR)中編譯和執行這些檔案。 要使用 AspectJ 進行編譯,將需要顯式地指定希望在給定編譯中包含的原始檔(Aspect 和類),ajc不象javac那樣簡單地為相關匯入模組搜尋類路徑。之所以這樣做,是因為標準 Java 應用程式中的每個類都是相對分離的元件。為了正確操作,一個類只要求其直接引用的類的存在。Aspect 表示跨越多個類的行為的聚集。因此,需要將 AOP 程式作為一個單元來編譯,而不能每次編譯一個類。 AspectJ 當前版本的一個重要限制是其編譯器只能將aspect加入到它擁有原始碼的程式碼中。也就是說,不能使用ajc將Advice新增到預編譯類中。AspectJ 團隊認為這個限制只是暫時的,AspectJ 網站承諾未來的版本(正式版 2.0)將允許位元組碼的修改。     AspectJ發行版包含了幾種開發工具。這預示著 AspectJ 將有美好的前景,因為它表明了作者對這一部分的一個重要承諾,使 AspectJ 對於開發人員將是友好的。對於面向 Aspect 的系統工具支援尤其重要,因為程式模組可能受到它們所未知的模組所影響。 隨 AspectJ 一起釋出的一個最重要的工具是圖形結構瀏覽器,它展示了 Aspect 如何與其它系統元件互動。這個結構瀏覽器既可以作為流行的 IDE 的外掛,也可以作為獨立的工具。圖3.4顯示了先前討論的日誌記錄示例的檢視。 aop3.4.jpg
圖3.4 AspectJ提供的“結構瀏覽器”工具 除了結構瀏覽器和核心編譯器之外,您還可以從 AspectJ 網站下載一個 Aspect 支援的偵錯程式、一個javadoc工具、一個Ant任務以及一個Emacs 外掛。 3.2.2 JBoss AOP研究 JBoss AOP關於AOP的實現與AspectJ是兩種完全不同的風格。由於Java利用元資料來儲存有關型別、方法、欄位的相關資訊,因此,可以通過Java提供的反射功能獲得模組相關的元資料,對方法進行攔截,並將被攔截的方法與aspect邏輯進行關聯。 3.2.2.1 攔截器(Interceptor) 在JBoss AOP中,是用攔截器來實現advice的。可以自定義攔截器,攔截方法呼叫、建構函式呼叫以及對欄位的訪問,但JBoss要求這些自定義的攔截器,必須實現org.jboss.aop.Interceptor介面: public interface Interceptor { public String getName(); public InvocationResponse invoke(Invocation invocation) throws Throwable; } 在JBoss AOP中,被攔截的欄位、構造器和方法均被轉化為通用的invoke方法呼叫。方法的引數接收一個Invocation物件,而方法的返回值、欄位的存取以及建構函式則被填入一個InvocationResponse物件。Invocation物件同時還驅動攔截鏈。下面我們自定義一個攔截器,它能夠攔截建構函式和方法的呼叫,並將跟蹤資訊列印到控制檯上: import org.jboss.aop.*; import java.lang.reflect.*; public class TracingInterceptor implements Interceptor { public String getName() { return TracingInterceptor; } public InvocationResponse invoke(Invocation invocation) throws Throwable { String message = null; if (invocation.getType() == InvocationType.METHOD) { Method method = MethodInvocation.getMethod(invocation); message = method: + method.getName(); } else if (invocation.getType() == InvocationType.CONSTRUCTOR) { Constructor c = ConstructorInvocation.getConstructor(invocation); message = constructor: + c.toString(); } else { // 不對欄位作處理,太繁瑣; return invocation.invokeNext(); } System.out.println(Entering + message); // 繼續。呼叫真正的方法或者建構函式 InvocationResponse rsp = invocation.invokeNext(); System.out.println(Leaving + message); return rsp; } } 在自定義的TracingInterceptor類中,invoke()方法對invocation的型別作判斷,以根據方法、建構函式和欄位型別,分別作出不同的操作。而其中,invocation.invokeNext()則表示通過一個攔截鏈獲得下一個invocation。 定義的攔截必須在xml檔案配置,使其繫結到具體的類。這個定義即為AOP中的切入點pointcut。例如具體的類為BusinessObject,則該pointcut在xml中的定義如下: <?xml version="1.0" encoding="UTF-8"> 上面的pointcut繫結TracingInterceptor到一個叫做BusinessObject的類。如果要將該Interceptor繫結到多個類,還可以利用正則表示式。例如,如果你想繫結由JVM載入的類,類表示式將變為 .*。如果你僅僅想跟蹤一個特定的包,那麼表示式將是bruce.zhang.mypackge.*。 當JBoss AOP獨立執行時,任何符合 META-INF/jboss-aop.xml模式的XML檔案將被JBoss AOP 執行期程式載入。如果相關的路徑被包含在任何JAR或你的CLASSPATH目錄中,該XML檔案將在啟動時,由JBoss AOP 執行期程式載入。 JBoss AOP還提供了過濾功能,可以通過在xml檔案中配置      過濾的標誌,使一些特定的方法(包括欄位的訪問)被過濾,從而不再執行Interceptor的相關邏輯。例如,我們要過濾BusinessObject類的所有get()和set()方法,以及main()方法,則可以修改上述的xml檔案: <?xml version="1.0" encoding="UTF-8"> true true 相應的,Interceptor程式碼也應作相關的修改,使其能夠識別配置檔案中的filter屬性: public class TracingInterceptor implements Interceptor { ……//getName()方法略; public InvocationResponse invoke(Invocation invocation) throws Throwable { String filter=(String)invocation.getMetaData(tracing, filter); if (filter != null && filter.equals(true)) return invocation.invokeNext();        ……//後面的程式碼略; } } 3.2.2.2 引入(Introduction) JBoss AOP同樣提供introduction功能,通過它,就可以為現有的類引入第三方介面或類的API了。例如,我們可以為具體的類如BusinessObject提供Tracing的開關,使得BusinessObject物件能夠根據具體的情況開啟或關閉aspect的Tracing功能。為實現該功能,可以定義一個Tracing介面: public interface Tracing { void enableTracing(); void disableTracing(); } 接下來需要定義一個混合類,它實現了介面Tracing。當BusinessObject類被例項化時,該混合類的例項就會被繫結到BusinessObject上。實現方法如下: import org.jboss.aop.Advised; public class TracingMixin implements Tracing { Advised advised; Public TracingMixin(Object obj) { this.advised = (Advised)obj; } public void enableTracing() { advised._getInstanceAdvisor().getMetaData().addMetaData ("tracing", "filter", true); } public void disableTracing() { advised._getInstanceAdvisor().getMetaData().addMetaData ("tracing", "filter", false); } } enableTracing()方法將filter屬性繫結到物件例項。disableTracing()方法作同樣的事,但是將filter屬性設定為false。 定義了Tracing介面和實現了該介面的混合類後,就可以在xml檔案中定義一個pointcut,強制BusinessObject類實現Tracing介面: <?xml version="1.0" encoding="UTF-8"> Tracing TracingMixin new TracingMixin(this) 注意xml檔案中的 標籤,它代表的含義是當BusinessObject物件被例項化時,將執行該標籤內的程式碼。以本例而言,當建立BusinessObject物件時,一個TracingMixin類的例項將被建立。任何單行的Java程式碼都可以放到 標籤中。 通過“引入(introduction)”功能,在處理BusinessObject物件時,就可以視其為Tracing介面型別而進行操作了,如下的示例: public class BusinessObject { public BusinessObject () {} public void helloWorld() { System.out.println(Hello World!); } public static void main(String[] args) { BusinessObject bo = new BusinessObject (); Tracing trace = (Tracing)this; bo.helloWorld(); System.out.println("Turn off tracing."); trace.disableTracing(); bo.helloWorld(); System.out.println("Turn on tracing."); trace.enableTracing(); bo.helloWorld(); } } 注意如下程式碼: Tracing trace = (Tracing)this; 此時this代表的即為BusinessObject,從Java程式碼的角度來看,由於BusinessObject並沒有實現Tracing介面,因此這行程式碼所示的顯式轉換為Tracing型別是不成功的。但通過“引入”功能,使得BusinessObject通過混合類,實現了Tracing介面,從而使得如上的程式碼能夠順利執行。隱含的意義就是,我們沒有修改BusinessObject的定義,而是通過AOP技術,為BusinessObject擴充套件實現了第三方提供的介面Tracing。
3.2.3 Spring AOP研究 Spring AOP使用純Java實現,不需要特別的編譯過程,也不需要控制類裝載層次。與JBoss AOP相同,它仍然利用了攔截器完成對方法的攔截。然而,Spring AOP實現AOP的主要技術卻主要來自於AOP聯盟,如攔截器應實現org.aopalliance.intercept.MethodInterceptor 介面,而所有advice必須實現org.aopalliance.aop.Advice標籤介面。此外,Spring實現AOP的目標也不同於其他大部分AOP框架,它的目標不是提供及其完善的AOP實現,而是提供一個和Spring IoC緊密整合的AOP實現,幫助解決企業應用 中的常見問題。因此,Spring AOP的功能通常是和Spring IoC容器聯合使用的。AOP Advice是用普通的bean定義語法來定義的,Advice和pointcut本身由Spring IoC 管理。這是一個重要的其他AOP實現的區別。 3.2.3.1 切入點(pointcut) Spring的切入點模型能夠使pointcut獨立於advice型別被重用。同樣的pointcut有可能接受不同的advice。將Pointcut介面分成兩個部分有利於重用類和方法的匹配部分,並且組合細粒度的操作(如和另一個方法匹配器執行一個“並”的操作)。 在Spring的切入點中,org.springframework.aop.Pointcut介面是重要的介面,它用來指定通知到特定的類和方法目標。完整的介面定義如下: public interface Pointcut {     ClassFilter getClassFilter();     MethodMatcher getMethodMatcher(); } ClassFilte型別也是一個介面,該介面被用來將切入點限制到一個給定的目標類的集合。 如果matches()永遠返回true,所有的目標類都將被匹配。 public interface ClassFilter {     boolean matches(Class clazz); } MethodMatcher介面通常更加重要。完整的介面定義如下: public interface MethodMatcher {     boolean matches(Method m, Class targetClass);     boolean matches(Method m, Class targetClass, Object[] args);     boolean isRuntime(); } matches(Method, Class) 方法被用來測試這個切入點是否匹配目標類的給定方法。這個測試可以在AOP代理建立的時候執行,避免在所有方法呼叫時都需要進行 測試。如果2個引數的matches()方法對某個方法返回true,並且MethodMatcher的isRuntime()也返回true,那麼3個引數的matches()方法將在每次方法呼叫的時候被呼叫。這使切入點能夠在目標advice被執行之前立即檢視傳遞給方法呼叫的引數。由於大部分MethodMatcher都是靜態的,意味著isRuntime()方法會返回false。此種情況下,3個引數的matches()方法永遠不會被呼叫。 Spring AOP提供了幾個實用的切入點實現,其中較為常用的是正則表示式切入點:org.springframework.aop.support.RegexpMethodPointcut,它使用Perl 5的正則表示式的語法。使用這個類你可以定義一個模式的列表。如果任何一個匹配,那個切入點將被計算成 true。用法如下: < SPAN>     class="org.springframework.aop.support.RegexpMethodPointcut">             .*get.*             .*absquatulate 不過,更多情況下是直接使用RegexpMethodPointcut一個實用子類: RegexpMethodPointcutAdvisor。它允許我們同時引用一個advice(在Spring AOP中,advice可以是攔截器,也可以是before advice,throws advice等)。這就簡化了bean的裝配,因為一個bean可以同時當作pointcut和advice,如下所示: class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> .*save.* .*do.* 注意配置檔案中的myPointcutAdvisor,在Spring AOP中,一個advisor就是一個aspect完整的模組化表示。通過advisor,可以將pointcut和advice(在此處即為MyInterceptor)繫結起來。 3.2.3.2 通知(advice) Spring AOP的advice可以跨越多個被advice物件共享,或者每個被advice物件有自己的advice。要實現advice,最簡單的做法就是定義一個攔截器(Interceptor)。它採用了AOP聯盟(AOP Alliance)的通用AOP介面(介面定義為aopalliance.jar)。要實現advice,需要實現aopalliance.jar中定義的MethodInterceptor介面。 例如,我們定義了一個業務物件介面BusinessObject及其實現類BusinessObjectImpl,該業務物件能夠儲存資料,其定義如下: public interface BusinessObject {     public void save(); } public class BusinessObjectImpl implements BusinessObject {            public void save() {                System.out.println("saving domain object......");     } } 現在需要為業務物件BusinessObject的Save()方法,提供Lock機制。根據Spring AOP的實現方式,我們可以定義一個LockInterceptor來實現MethodInterceptor介面: import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class LockInterceptor implements MethodInterceptor {     public Object invoke(MethodInvocation invocation) throws Throwable    {         // TODO Auto-generated method stub         lock();         Object ret= invocation.proceed();         unlock();         return ret;     } private void lock() {         System.out.println("lock domain object...");     } private void unlock() {         System.out.println("unlock domain object...");     } } 為將interceptor與具體的advice繫結起來,需要在配置檔案中配置bean: 3.2.3.3 AOP代理與IoC容器 由於Spring中提供了IoC容器(例如BeanFactory),因此我們可以通過Ioc機制,利用ProxyFactoryBean來建立AOP代理。ProxyFactoryBean和其他Spring的 FactoryBean實現一樣,引入一個間接的層次。如果你定義一個名字為foo的ProxyFactoryBean,引用foo的物件所看到的不是ProxyFactoryBean例項本身,而是由實現ProxyFactoryBean的類的 getObject()方法所建立的物件。這個方法將建立一個包裝了目標物件 的AOP代理。 AOP代理利用的是Java的動態代理技術,通過它就可以載入並執行AOP元件。同時,還需要通過IoC的方式將advice注入到介面以及其實現類。以前面的業務物件BusinessObject為例,在xml配置檔案中的配置如下:          test.aop.spring.BusinessObject          myPointcutAdvisor 通過上述對pointcut、advice、advisor和AOP代理的配置,我們就可以輕易地在Spring中實現AOP,例如: import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class App {     private BusinessObject bo = null; public static void main(String[] args) { ApplicationContext ctx=new FileSystemXmlApplicationContext("Bean.xml");                bo= (BusinessObject) ctx.getBean("myAOPProxy");                      bo.save(); } } 首先,通過AOP代理獲得BusinessObject物件。當呼叫BusinessObject物件的save()方法時,攔截器LockInterceptor根據RegexpMethodPointcutAdvisor配置的pointcut和advice之間的關係,判定該方法的呼叫為join point,從而攔截該方法呼叫,並注入advice的執行邏輯,即lock()和unlock(),最終實現了AOP。 3.2.3.4 引入(introduction)     在Spring AOP中,將introduction當作advice來處理。與一般的advice一樣,introduction advice相當於一種特殊型別的攔截通知,需要實現IntroductionAdvisor和IntroductionInterceptor介面,而IntroductionInterceptor介面繼承自MethodInterceptor: public interface IntroductionInterceptor extends MethodInterceptor {     boolean implementsInterface(Class intf); } Introduction通知不能被用於任何pointcut,因為它只能作用於類層次上,而不是方法。我們可以只用InterceptionIntroductionAdvisor來實現匯入通知,它有下面的方法: public interface InterceptionIntroductionAdvisor extends InterceptionAdvisor {     ClassFilter getClassFilter();     IntroductionInterceptor getIntroductionInterceptor();     Class[] getInterfaces(); } 接下來,我以JBoss AOP一節中的例子來說明introduction在Spring AOP中的應用。我們的目標仍然是為一個已有的業務物件引入第三方介面Tracing: public interface Tracing { void enableTracing(); void disableTracing(); boolean enabled(); } 首先,我們需要一個做大量轉化的IntroductionInterceptor。在這裡,我們繼承 org.springframework.aop.support.DelegatingIntroductionInterceptor 實現類。當然我們可以直接實現IntroductionInterceptor介面,但是大多數情況下 DelegatingIntroductionInterceptor是最合適的。 DelegatingIntroductionInterceptor的設計是將introduction委託到真正實現introduction介面的介面,隱藏完成這些工作的攔截器。委託可以使用構造方法引數設定到任何物件中;預設的委託就是自己(當無引數的構造方法被使用時)。這樣在下面的例子裡,委託是DelegatingIntroductionInterceptor的子類 TracingMixin。給定一個委託(預設是自身)的 DelegatingIntroductionInterceptor例項尋找被這個委託(而不是IntroductionInterceptor)實現的所有介面,並支援它們中任何一個匯入。子類如TracingMixi也可能呼叫suppressInterflace(Class intf) 方法來隱藏不應暴露的介面。然而,不管IntroductionInterceptor 準備支援多少介面,IntroductionAdvisor將控制哪個介面將被實際暴露。一個匯入的介面將隱藏目標的同一個介面的所有實現。 這樣,TracingMixin繼承DelegatingIntroductionInterceptor並自己實現介面Tracing。父類自動選擇支援introduction的Tracing,所以我們不需要指定它。用這種方法我們可以匯入任意數量的介面。 public class TracingMixin extends DelegatingIntroductionInterceptor implements Tracing {     private boolean enabled;     public void enableTracing () {         this.enabled = true;     }     public void disableTracing () {         this. enabled = false;     }     public boolean enabled() {         return this.enabled;     }     public Object invoke(MethodInvocation invocation) throws Throwable   {               return super.invoke(invocation);     } } 通常不要需要改寫invoke()方法:實現DelegatingIntroductionInterceptor就足夠了,如果是引入的方法,DelegatingIntroductionInterceptor實現會呼叫委託方法, 否則繼續沿著連線點處理。 所需的introduction advisor是很簡單的。只需儲存一個獨立的TracingMixin例項,並指定匯入的介面,在這裡就是Tracing。此時,TracingMixin沒有相關配置,所以我們簡單地使用new來建立它。 public class TracingMixinAdvisor extends DefaultIntroductionAdvisor {     public TracingMixinAdvisor() {         super(new TracingMixin(),Tracing.class);     } } 我們可以非常簡單地使用這個advisor。它不需要任何配置。(但是,有一點是必要的:就是不可能在沒有IntroductionAdvisor 的情況下使用IntroductionInterceptor。) 和引入一樣,通常 advisor必須是針對每個例項的,並且是有狀態的。我們會有不同的TracingMixinAdvisor。每個被通知物件,會有不同的TracingMixin。advisor組成了被通知物件的狀態的一部分。 在Spring中,Spring AOP的核心API已經基本穩定了。和Spring的其它部分一樣, AOP框架是模組化的,在保留基礎設計的同時提供擴充套件。在Spring 1.1到1.2階段有很多地方可能會有所提高,但是這些地方也保留了向後相容性。它們是: (一)效能的提高:AOP代理的建立由工廠通過策略介面處理。因此能夠支援額外的AOP 代理型別而不影響使用者程式碼或核心實現。 (二)更具表達力的pointcut:Spring目前提供了一個具有表達力的切入點介面,同時添加了更多的切入點實現。Spring正在考慮提供一個簡單但具有強大表達式語言的實現。

相關推薦

Java平臺AOP技術研究

3.1 Java平臺AOP技術概覽 3.1.1 AOP技術在Java平臺中的應用 AOP在實驗室應用和商業應用上,Java平臺始終走在前面。從最初也是目前最成熟的AOP工具——AspectJ,到目前已經融和在企業級

Java核心技術Java平臺的理解

巨集觀角度: 跟c/c++最大的不同點在於,c/c++程式設計是面向作業系統的,需要開發者極大地關心不同作業系統之間的差異性;而Java平臺通過虛擬機器遮蔽了作業系統的底層細節,使得開發者無需過多地關心不同作業系統之間的差異性。 通過增加一個間接的中間層來進行”解耦“是計算機領域非常常用的一種”

JAVA刷網站流量的技術研究總結

經過幾天的開發和測試,最終用JAVA實現了網站刷流量的程式,用到的技術SWT、JavaXPCOM。刷流量程式能利用多個代理IP,重新整理指定的多個網頁,並且一旦啟動後能穩定執行。這裡總結下用到的工具和技術。 本文為數飛OA工程師的技術探索和原創博文,首發於51CTO,轉載請註明出處。 基礎工具方面

對於Java後端技術自己做的還不夠好!更多Java學習平臺請看這裡!

純Java領域公眾號推薦,也是我一直關注的公眾號,這裡資源、面試、技術、進階應有盡有,小白到架構師,都能有所收穫,職業道路上有他們陪伴,相信你必不會孤單。想把它們一網打盡嗎! Java面試那些

基於大資料分析的安全管理平臺技術研究及應用

http://www.venustech.com.cn/NewsInfo/531/25566.Html 【內容摘要】本文首先通過介紹大資料的起因,給出了大資料的定義和特徵描述,並簡要說明了當前大資料的研究概況。接下來,本文闡釋了大資料分析技術,對大資料在資訊保安領域尤其是安全管理平臺領域的應用做了深入分

Java面試--Spring技術要點--Spring AOP(面向切面程式設計)

33  Spring AOP的概念 面向切面的程式設計,是一種程式設計技術,是OOP(面向物件程式設計)的補充和完善。OOP的執行是一種從上往下的流程,並沒有從左到右的關係。因此在OOP程式設計中,會

【明天的地平線】專注Java相關技術:SpringBoot、Spr ingCloud、MyBatis、Docker、微服務、叢集、分散式、 Linux、Jenkins、Netty、Angular 5 、Vue 2、微信小程式、程式碼生成器等的技術研究和乾貨分

專注Java相關技術:SpringBoot、Spr ingCloud、MyBatis、Docker、微服務、叢集、分散式、 Linux、Jenkins、Netty、Angular 5 、Vue 2、微...

Java呼叫語言技術平臺(LTP) 3.4.0進行漢語文字處理經驗總結

1. 語言技術平臺(LTP)介紹https://www.ltp-cloud.com//Users/zhangyongwei/Downloads/ltp4j2/ltp4j/ltp4j2/ltp4j/target/nar/ltp4j-0.1.0-SNAPSHOT-x86_64-M

AOP技術應用和研究--AOP簡單應用

           為了更好的理解AOP實踐和體現AOP的優勢,我們始終將OOP和AOP的比較貫穿到下文中。並在最終總結出AOP與OOP相比所擁有的優點,AOP的缺點以及AOP一般的使用場景。 1.1 問題空間到解空間的對映 在比較研究OOP和AOP實踐之前,先讓解決從

[Java JVM] Hotspot GC研究- 64位引用指標壓縮技術

為什麼需要指標壓縮 在上一篇文章 [Java JVM] Hotspot GC研究- 開篇&物件記憶體佈局 中介紹物件記憶體佈局時, 曾提到過, 由於在64位CPU下, 指標的寬度是64位的, 而實際的heap區域遠遠用不到這麼大的記憶體, 使用64b

JAVA 開發平臺技術和框架(五)日誌管理 :common-logging slf4j log4j logBack

common-logging common-logging是apache提供的一個通用的日誌介面。使用者可以自由選擇第三方的日誌元件作為具體實現,像log4j,或者jdk自帶的logging, common-logging會通過動態查詢的機制,在程式執行時自動找出真正使用

通過例子全面解析Java中的Aop技術-包括靜態代理,動態代理,Spring Aop(全面解析,附帶全部原始碼,小白看一遍也可以理解)

AOP概述(Aspect Oriented Programming)所謂AOP,就是面向方面(切面)的程式設計,簡單來說,就是通過面向切面,在執行的方法前後加上所需要實現的事情,比如,日誌,計算方法執行的時間,實現事務等。這樣做的目的一方面在於不改變原有程式碼,提高通用性,最

R語言統計分析技術研究——嶺回歸技術的原理和應用

gts 根據 誤差 med 分享 jce not -c rt4 嶺回歸技術的原理和應用

Android中apk動態載入技術研究(2)android插件化及實現

name creat package path iss fontsize 調用 dex con 了解了android中類載入的前期知識點後,來看看android中DexClassLoader詳細的實現 詳細載入流程例如以下: 宿主程序會到文件系統比

微軟平臺開發技術藍圖

times rgb pos 評論 track 平臺開發 .cn article height 近期項目面臨技術選型,忙裏偷閑畫了一幅微軟平臺開發技術藍圖。因為時間有限,還有非常多不完好的地方,分享出來。僅僅是希望能給須要的朋友一些參考(沒有時間寫相關的介紹也沒有

java書籍推薦:《Java SE 6 技術手冊》

rate rst tail right 5.1 important hid 新手 log Java SE 6 技術手冊 或 Java SE 6 技術手冊 Java SE 6 技術手冊 為什麼選擇用 Markdown?僅僅是單純把文件

AOP技術分析

java 復制 oop dmi ase 框架 cts 動態 mls AOP的概述(http://www.cnblogs.com/lxp503238/p/6837653.html) 1. 什麽是AOP的技術? * 在軟件業,AOP為Aspect O

簡介二:操作系統和集群開源技術研究

pla 設計 成本 期貨 jit cin 虛擬化技術 特性 基礎 作者:王步宙、陳晨 2008年似乎是個有魔力的一年,在這一年裏大西洋兩岸的德國和紐約兩大證券交易所集團幾乎不約而同的啟動了交易系統開源化項目。對於開源項目的動因,有人提出成本論,有人提出是低延遲論,我們認為可

JAVA秒會技術之秒殺面試官】秒殺Java面試官——集合篇(一)

tails category tail java cat 秒殺 試題 面試官 java面試 【JAVA秒會技術之秒殺面試官】秒殺Java面試官——集合篇(一) 【JAVA秒會技術之秒殺面試官】JavaEE常見面試題(三) http://blog.csdn.net/qq296

Java序列化技術

exc java 姓名 clas () ati private tac 對象 用序列化來存儲對象。 先定義一個用來序列化的類: package io.xuliehua; import java.io.*; public class Student implements