精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Spring AOP 實(shí)現(xiàn)原理與CGLIB應(yīng)用

開(kāi)發(fā) 后端
AOP(Aspect Orient Programming),作為面向?qū)ο缶幊痰囊环N補(bǔ)充,廣泛應(yīng)用于處理一些具有橫切性質(zhì)的系統(tǒng)級(jí)服務(wù),如事務(wù)管理、安全檢查、緩存、對(duì)象池管理等。

簡(jiǎn)介: AOP(Aspect Orient Programming),也就是面向方面編程,作為面向?qū)ο缶幊痰囊环N補(bǔ)充,專(zhuān)門(mén)用于處理系統(tǒng)中分布于各個(gè)模塊(不同方法)中的交叉關(guān)注點(diǎn)的問(wèn)題,在 Java EE 應(yīng)用中,常常通過(guò) AOP 來(lái)處理一些具有橫切性質(zhì)的系統(tǒng)級(jí)服務(wù),如事務(wù)管理、安全檢查、緩存、對(duì)象池管理等。AOP 實(shí)現(xiàn)的關(guān)鍵就在于 AOP 框架自動(dòng)創(chuàng)建的 AOP 代理,AOP 代理主要分為靜態(tài)代理和動(dòng)態(tài)代理兩大類(lèi),靜態(tài)代理以 AspectJ 為代表;而動(dòng)態(tài)代理則以 Spring AOP 為代表。本文會(huì)從 AspectJ 分析起,逐漸深入,并介紹 CGLIB 來(lái)介紹 Spring AOP 框架的實(shí)現(xiàn)原理。

AOP(Aspect Orient Programming),作為面向?qū)ο缶幊痰囊环N補(bǔ)充,廣泛應(yīng)用于處理一些具有橫切性質(zhì)的系統(tǒng)級(jí)服務(wù),如事務(wù)管理、安全檢查、緩存、對(duì)象池管理等。 AOP 實(shí)現(xiàn)的關(guān)鍵就在于 AOP 框架自動(dòng)創(chuàng)建的 AOP 代理,AOP 代理則可分為靜態(tài)代理和動(dòng)態(tài)代理兩大類(lèi),其中靜態(tài)代理是指使用 AOP 框架提供的命令進(jìn)行編譯,從而在編譯階段就可生成 AOP 代理類(lèi),因此也稱(chēng)為編譯時(shí)增強(qiáng);而動(dòng)態(tài)代理則在運(yùn)行時(shí)借助于 JDK 動(dòng)態(tài)代理、CGLIB 等在內(nèi)存中“臨時(shí)”生成 AOP 動(dòng)態(tài)代理類(lèi),因此也被稱(chēng)為運(yùn)行時(shí)增強(qiáng)。

Spring AOP 實(shí)現(xiàn)原理與 CGLIB 應(yīng)用

AOP 的存在價(jià)值

在傳統(tǒng) OOP 編程里以對(duì)象為核心,整個(gè)軟件系統(tǒng)由系列相互依賴(lài)的對(duì)象所組成,而這些對(duì)象將被抽象成一個(gè)一個(gè)的類(lèi),并允許使用類(lèi)繼承來(lái)管理類(lèi)與類(lèi)之間一般到特殊的關(guān)系。隨著軟件規(guī)模的增大,應(yīng)用的逐漸升級(jí),慢慢出現(xiàn)了一些 OOP 很難解決的問(wèn)題。

我們可以通過(guò)分析、抽象出一系列具有一定屬性與行為的對(duì)象,并通過(guò)這些對(duì)象之間的協(xié)作來(lái)形成一個(gè)完整的軟件功能。由于對(duì)象可以繼承,因此我們可以把具有相同功能或相同特性的屬性抽象到一個(gè)層次分明的類(lèi)結(jié)構(gòu)體系中。隨著軟件規(guī)范的不斷擴(kuò)大,專(zhuān)業(yè)化分工越來(lái)越系列,以及 OOP 應(yīng)用實(shí)踐的不斷增多,隨之也暴露出了一些 OOP 無(wú)法很好解決的問(wèn)題。

現(xiàn)在假設(shè)系統(tǒng)中有 3 段完全相似的代碼,這些代碼通常會(huì)采用“復(fù)制”、“粘貼”方式來(lái)完成,通過(guò)這種“復(fù)制”、“粘貼”方式開(kāi)發(fā)出來(lái)的軟件如圖 1 所示。

圖 1.多個(gè)地方包含相同代碼的軟件

Spring AOP 實(shí)現(xiàn)原理與 CGLIB 應(yīng)用

看到如圖 1 所示的示意圖,可能有的讀者已經(jīng)發(fā)現(xiàn)了這種做法的不足之處:如果有一天,圖 1 中的深色代碼段需要修改,那是不是要打開(kāi) 3 個(gè)地方的代碼進(jìn)行修改?如果不是 3 個(gè)地方包含這段代碼,而是 100 個(gè)地方,甚至是 1000 個(gè)地方包含這段代碼段,那會(huì)是什么后果?

為了解決這個(gè)問(wèn)題,我們通常會(huì)采用將如圖 1 所示的深色代碼部分定義成一個(gè)方法,然后在 3 個(gè)代碼段中分別調(diào)用該方法即可。在這種方式下,軟件系統(tǒng)的結(jié)構(gòu)如圖 2 所示。

圖 2 通過(guò)方法調(diào)用實(shí)現(xiàn)系統(tǒng)功能

Spring AOP 實(shí)現(xiàn)原理與 CGLIB 應(yīng)用

對(duì)于如圖 2 所示的軟件系統(tǒng),如果需要修改深色部分的代碼,只要修改一個(gè)地方即可,不管整個(gè)系統(tǒng)中有多少地方調(diào)用了該方法,程序無(wú)須修改這些地方,只需修改被調(diào)用的方法即可——通過(guò)這種方式,大大降低了軟件后期維護(hù)的復(fù)雜度。

對(duì)于如圖 2 所示的方法 1、方法 2、方法 3 依然需要顯式調(diào)用深色方法,這樣做能夠解決大部分應(yīng)用場(chǎng)景。但對(duì)于一些更特殊的情況:應(yīng)用需要方法 1、方法 2、方法 3 徹底與深色方法分離——方法 1、方法 2、方法 3 無(wú)須直接調(diào)用深色方法,那如何解決?

因?yàn)檐浖到y(tǒng)需求變更是很頻繁的事情,系統(tǒng)前期設(shè)計(jì)方法 1、方法 2、方法 3 時(shí)只實(shí)現(xiàn)了核心業(yè)務(wù)功能,過(guò)了一段時(shí)間,我們需要為方法 1、方法 2、方法 3 都增加事務(wù)控制;又過(guò)了一段時(shí)間,客戶(hù)提出方法 1、方法 2、方法 3 需要進(jìn)行用戶(hù)合法性驗(yàn)證,只有合法的用戶(hù)才能執(zhí)行這些方法;又過(guò)了一段時(shí)間,客戶(hù)又提出方法 1、方法 2、方法 3 應(yīng)該增加日志記錄;又過(guò)了一段時(shí)間,客戶(hù)又提出……面對(duì)這樣的情況,我們?cè)趺崔k?通常有兩種做法:

根據(jù)需求說(shuō)明書(shū),直接拒絕客戶(hù)要求;擁抱需求,滿(mǎn)足客戶(hù)的需求。

***種做法顯然不好,客戶(hù)是上帝,我們應(yīng)該盡量滿(mǎn)足客戶(hù)的需求。通常會(huì)采用第二種做法,那如何解決呢?是不是每次先定義一個(gè)新方法,然后修改方法 1、方法 2、方法 3,增加調(diào)用新方法?這樣做的工作量也不小啊!我們希望有一種特殊的方法:我們只要定義該方法,無(wú)須在方法 1、方法 2、方法 3 中顯式調(diào)用它,系統(tǒng)會(huì)“自動(dòng)”執(zhí)行該特殊方法。

上面想法聽(tīng)起來(lái)很神奇,甚至有一些不切實(shí)際,但其實(shí)是完全可以實(shí)現(xiàn)的,實(shí)現(xiàn)這個(gè)需求的技術(shù)就是 AOP。AOP 專(zhuān)門(mén)用于處理系統(tǒng)中分布于各個(gè)模塊(不同方法)中的交叉關(guān)注點(diǎn)的問(wèn)題,在 Java EE 應(yīng)用中,常常通過(guò) AOP 來(lái)處理一些具有橫切性質(zhì)的系統(tǒng)級(jí)服務(wù),如事務(wù)管理、安全檢查、緩存、對(duì)象池管理等,AOP 已經(jīng)成為一種非常常用的解決方案。

使用 AspectJ 的編譯時(shí)增強(qiáng)進(jìn)行 AOP

AspectJ 是一個(gè)基于 Java 語(yǔ)言的 AOP 框架,提供了強(qiáng)大的 AOP 功能,其他很多 AOP 框架都借鑒或采納其中的一些思想。

AspectJ 是 Java 語(yǔ)言的一個(gè) AOP 實(shí)現(xiàn),其主要包括兩個(gè)部分:***個(gè)部分定義了如何表達(dá)、定義 AOP 編程中的語(yǔ)法規(guī)范,通過(guò)這套語(yǔ)言規(guī)范,我們可以方便地用 AOP 來(lái)解決 Java 語(yǔ)言中存在的交叉關(guān)注點(diǎn)問(wèn)題;另一個(gè)部分是工具部分,包括編譯器、調(diào)試工具等。

AspectJ 是最早、功能比較強(qiáng)大的 AOP 實(shí)現(xiàn)之一,對(duì)整套 AOP 機(jī)制都有較好的實(shí)現(xiàn),很多其他語(yǔ)言的 AOP 實(shí)現(xiàn),也借鑒或采納了 AspectJ 中很多設(shè)計(jì)。在 Java 領(lǐng)域,AspectJ 中的很多語(yǔ)法結(jié)構(gòu)基本上已成為 AOP 領(lǐng)域的標(biāo)準(zhǔn)。

下載、安裝 AspectJ 比較簡(jiǎn)單,讀者登錄 AspectJ 官網(wǎng)(http://www.eclipse.org/aspectj),即可下載到一個(gè)可執(zhí)行的 JAR 包,使用 java -jar aspectj-1.x.x.jar 命令、多次單擊“Next”按鈕即可成功安裝 AspectJ。

成功安裝了 AspectJ 之后,將會(huì)在 E:\Java\AOP\aspectj1.6 路徑下(AspectJ 的安裝路徑)看到如下文件結(jié)構(gòu):

bin:該路徑下存放了 aj、aj5、ajc、ajdoc、ajbrowser 等命令,其中 ajc 命令最常用,它的作用類(lèi)似于 javac,用于對(duì)普通 Java 類(lèi)進(jìn)行編譯時(shí)增強(qiáng)。

docs:該路徑下存放了 AspectJ 的使用說(shuō)明、參考手冊(cè)、API 文檔等文檔。

lib:該路徑下的 4 個(gè) JAR 文件是 AspectJ 的核心類(lèi)庫(kù)。

相關(guān)授權(quán)文件。

一些文檔、AspectJ 入門(mén)書(shū)籍,一談到使用 AspectJ,就認(rèn)為必須使用 Eclipse 工具,似乎離開(kāi)了該工具就無(wú)法使用 AspectJ 了。

雖然 AspectJ 是 Eclipse 基金組織的開(kāi)源項(xiàng)目,而且提供了 Eclipse 的 AJDT 插件(AspectJ Development Tools)來(lái)開(kāi)發(fā) AspectJ 應(yīng)用,但 AspectJ 絕對(duì)無(wú)須依賴(lài)于 Eclipse 工具。

實(shí)際上,AspectJ 的用法非常簡(jiǎn)單,就像我們使用 JDK 編譯、運(yùn)行 Java 程序一樣。下面通過(guò)一個(gè)簡(jiǎn)單的程序來(lái)示范 AspectJ 的用法,并分析 AspectJ 如何在編譯時(shí)進(jìn)行增強(qiáng)。

首先編寫(xiě)一個(gè)簡(jiǎn)單的 Java 類(lèi),這個(gè) Java 類(lèi)用于模擬一個(gè)業(yè)務(wù)組件。

清單 1.Hello.java

  1. public class Hello   
  2.  {   
  3.  // 定義一個(gè)簡(jiǎn)單方法,模擬應(yīng)用中的業(yè)務(wù)邏輯方法  
  4.  public void sayHello()  
  5.    {  
  6.    System.out.println("Hello AspectJ!");  
  7.    }  
  8.  // 主方法,程序的入口  
  9.  public static void main(String[] args)   
  10.  {   
  11.  Hello h = new Hello();   
  12.  h.sayHello();   
  13.  }   
  14.  } 

上面 Hello 類(lèi)模擬了一個(gè)業(yè)務(wù)邏輯組件,編譯、運(yùn)行該 Java 程序,這個(gè)結(jié)果是沒(méi)有任何懸念的,程序?qū)⒃诳刂婆_(tái)打印“Hello AspectJ”字符串。

假設(shè)現(xiàn)在客戶(hù)需要在執(zhí)行 sayHello() 方法之前啟動(dòng)事務(wù),當(dāng)該方法執(zhí)行結(jié)束時(shí)關(guān)閉事務(wù),在傳統(tǒng)編程模式下,我們必須手動(dòng)修改 sayHello() 方法——如果改為使用 AspectJ,則可以無(wú)須修改上面的 sayHello() 方法。

下面我們定義一個(gè)特殊的 Java 類(lèi)。

清單 2.TxAspect.java

  1. public aspect TxAspect   
  2.  {   
  3.  // 指定執(zhí)行 Hello.sayHello() 方法時(shí)執(zhí)行下面代碼塊  
  4.  void around():call(void Hello.sayHello())  
  5.  {  
  6.  System.out.println("開(kāi)始事務(wù) ...");  
  7.  proceed();  
  8.  System.out.println("事務(wù)結(jié)束 ...");  
  9.  }  
  10.  } 

可能讀者已經(jīng)發(fā)現(xiàn)了,上面類(lèi)文件中不是使用 class、interface、enum 在定義 Java 類(lèi),而是使用了 aspect ——難道 Java 語(yǔ)言又新增了關(guān)鍵字?沒(méi)有!上面的 TxAspect 根本不是一個(gè) Java 類(lèi),所以 aspect 也不是 Java 支持的關(guān)鍵字,它只是 AspectJ 才能識(shí)別的關(guān)鍵字。

上面粗體字代碼也不是方法,它只是指定當(dāng)程序執(zhí)行 Hello 對(duì)象的 sayHello() 方法時(shí),系統(tǒng)將改為執(zhí)行粗體字代碼的花括號(hào)代碼塊,其中 proceed() 代表回調(diào)原來(lái)的 sayHello() 方法。

正如前面提到的,Java 無(wú)法識(shí)別 TxAspect.java 文件的內(nèi)容,所以我們要使用 ajc.exe 命令來(lái)編譯上面的 Java 程序。為了能在命令行使用 ajc.exe 命令,需要把 AspectJ 安裝目錄下的 bin 路徑(比如 E:\Java\AOP\aspectj1.6\bin 目錄)添加到系統(tǒng)的 PATH 環(huán)境變量中。接下來(lái)執(zhí)行如下命令進(jìn)行編譯:

ajc -d . Hello.java TxAspect.java

我們可以把 ajc.exe 理解成 javac.exe 命令,都用于編譯 Java 程序,區(qū)別是 ajc.exe 命令可識(shí)別 AspectJ 的語(yǔ)法;從這個(gè)意義上看,我們可以將 ajc.exe 當(dāng)成一個(gè)增強(qiáng)版的 javac.exe 命令。

運(yùn)行該 Hello 類(lèi)依然無(wú)須任何改變,因?yàn)?Hello 類(lèi)位于 lee 包下。程序使用如下命令運(yùn)行 Hello 類(lèi):java lee.Hello

運(yùn)行該程序,將看到一個(gè)令人驚喜的結(jié)果:

開(kāi)始事務(wù) …

Hello AspectJ!

事務(wù)結(jié)束 …

從上面運(yùn)行結(jié)果來(lái)看,我們完全可以不對(duì) Hello.java 類(lèi)進(jìn)行任何修改,同時(shí)又可以滿(mǎn)足客戶(hù)的需求:上面程序只是在控制臺(tái)打印“開(kāi)始事務(wù) …”、“結(jié)束事務(wù) …”來(lái)模擬了事務(wù)操作,實(shí)際上我們可用實(shí)際的事務(wù)操作代碼來(lái)代替這兩行簡(jiǎn)單的語(yǔ)句,這就可以滿(mǎn)足客戶(hù)需求了。

如果客戶(hù)再次提出新需求,需要在 sayHello() 方法后增加記錄日志的功能,那也很簡(jiǎn)單,我們?cè)俣x一個(gè) LogAspect,程序如下:

清單 3.LogAspect.java

  1. public aspect LogAspect  
  2.  {  
  3.  // 定義一個(gè) PointCut,其名為 logPointcut  
  4.  // 該 PointCut 對(duì)應(yīng)于指定 Hello 對(duì)象的 sayHello 方法  
  5.      pointcut logPointcut() :execution(void Hello.sayHello());  
  6.  // 在 logPointcut 之后執(zhí)行下面代碼塊  
  7.      after():logPointcut()  
  8.      {  
  9.  System.out.println("記錄日志 ...");  
  10.      }  
  11.  } 

上面程序的粗體字代碼定義了一個(gè) Pointcut:logPointcut – 等同于執(zhí)行 Hello 對(duì)象的 sayHello() 方法,并指定在 logPointcut 之后執(zhí)行簡(jiǎn)單的代碼塊,也就是說(shuō),在 sayHello() 方法之后執(zhí)行指定代碼塊。使用如下命令來(lái)編譯上面的 Java 程序:ajc -d . *.java

再次運(yùn)行 Hello 類(lèi),將看到如下運(yùn)行結(jié)果:

開(kāi)始事務(wù) …

Hello AspectJ!

記錄日志 …

事務(wù)結(jié)束 …

從上面運(yùn)行結(jié)果來(lái)看,通過(guò)使用 AspectJ 提供的 AOP 支持,我們可以為 sayHello() 方法不斷增加新功能。

為什么在對(duì) Hello 類(lèi)沒(méi)有任何修改的前提下,而 Hello 類(lèi)能不斷地、動(dòng)態(tài)增加新功能呢?這看上去并不符合 Java 基本語(yǔ)法規(guī)則啊。實(shí)際上我們可以使用 Java 的反編譯工具來(lái)反編譯前面程序生成的 Hello.class 文件,發(fā)現(xiàn) Hello.class 文件的代碼如下:

清單 4.Hello.class

  1. package lee;   
  2.  import java.io.PrintStream;   
  3.  import org.aspectj.runtime.internal.AroundClosure;   
  4.    
  5.  public class Hello   
  6.  {   
  7.   public void sayHello()   
  8.   {   
  9.     try 
  10.     {   
  11.       System.out.println("Hello AspectJ!"); } catch (Throwable localThrowable) {   
  12.       LogAspect.aspectOf().ajc$after$lee_LogAspect$1$9fd5dd97(); throw localThrowable; }   
  13.       LogAspect.aspectOf().ajc$after$lee_LogAspect$1$9fd5dd97();   
  14.   }   
  15.   ...   
  16.   private static final void sayHello_aroundBody1$advice(Hello target,   
  17.              TxAspect ajc$aspectInstance, AroundClosure ajc$aroundClosure)   
  18.   {   
  19.     System.out.println("開(kāi)始事務(wù) ...");   
  20.     AroundClosure localAroundClosure = ajc$aroundClosure; sayHello_aroundBody0(target);   
  21.     System.out.println("事務(wù)結(jié)束 ...");   
  22.   }   
  23.  } 

不難發(fā)現(xiàn)這個(gè) Hello.class 文件不是由原來(lái)的 Hello.java 文件編譯得到的,該 Hello.class 里新增了很多內(nèi)容——這表明 AspectJ 在編譯時(shí)“自動(dòng)”編譯得到了一個(gè)新類(lèi),這個(gè)新類(lèi)增強(qiáng)了原有的 Hello.java 類(lèi)的功能,因此 AspectJ 通常被稱(chēng)為編譯時(shí)增強(qiáng)的 AOP 框架。

提示:與 AspectJ 相對(duì)的還有另外一種 AOP 框架,它們不需要在編譯時(shí)對(duì)目標(biāo)類(lèi)進(jìn)行增強(qiáng),而是運(yùn)行時(shí)生成目標(biāo)類(lèi)的代理類(lèi),該代理類(lèi)要么與目標(biāo)類(lèi)實(shí)現(xiàn)相同的接口,要么是目標(biāo)類(lèi)的子類(lèi)——總之,代理類(lèi)的實(shí)例可作為目標(biāo)類(lèi)的實(shí)例來(lái)使用。一般來(lái)說(shuō),編譯時(shí)增強(qiáng)的 AOP 框架在性能上更有優(yōu)勢(shì)——因?yàn)檫\(yùn)行時(shí)動(dòng)態(tài)增強(qiáng)的 AOP 框架需要每次運(yùn)行時(shí)都進(jìn)行動(dòng)態(tài)增強(qiáng)。

實(shí)際上,AspectJ 允許同時(shí)為多個(gè)方法添加新功能,只要我們定義 Pointcut 時(shí)指定匹配更多的方法即可。如下片段:

  1. pointcut xxxPointcut()   
  2.      :execution(void H*.say*()); 

上面程序中的 xxxPointcut 將可以匹配所有以 H 開(kāi)頭的類(lèi)中、所有以 say 開(kāi)頭的方法,但該方法返回的必須是 void;如果不想匹配任意的返回值類(lèi)型,則可將代碼改為如下形式:

  1. pointcut xxxPointcut()  
  2. :execution(* H*.say*());xxxPointcut() 

關(guān)于如何定義 AspectJ 中的 Aspect、Pointcut 等,讀者可以參考 AspectJ 安裝路徑下的 doc 目錄里的 quick5.pdf 文件。

使用 Spring AOP

與 AspectJ 相同的是,Spring AOP 同樣需要對(duì)目標(biāo)類(lèi)進(jìn)行增強(qiáng),也就是生成新的 AOP 代理類(lèi);與 AspectJ 不同的是,Spring AOP 無(wú)需使用任何特殊命令對(duì) Java 源代碼進(jìn)行編譯,它采用運(yùn)行時(shí)動(dòng)態(tài)地、在內(nèi)存中臨時(shí)生成“代理類(lèi)”的方式來(lái)生成 AOP 代理。

Spring 允許使用 AspectJ Annotation 用于定義方面(Aspect)、切入點(diǎn)(Pointcut)和增強(qiáng)處理(Advice),Spring 框架則可識(shí)別并根據(jù)這些 Annotation 來(lái)生成 AOP 代理。Spring 只是使用了和 AspectJ 5 一樣的注解,但并沒(méi)有使用 AspectJ 的編譯器或者織入器(Weaver),底層依然使用的是 Spring AOP,依然是在運(yùn)行時(shí)動(dòng)態(tài)生成 AOP 代理,并不依賴(lài)于 AspectJ 的編譯器或者織入器。

簡(jiǎn)單地說(shuō),Spring 依然采用運(yùn)行時(shí)生成動(dòng)態(tài)代理的方式來(lái)增強(qiáng)目標(biāo)對(duì)象,所以它不需要增加額外的編譯,也不需要 AspectJ 的織入器支持;而 AspectJ 在采用編譯時(shí)增強(qiáng),所以 AspectJ 需要使用自己的編譯器來(lái)編譯 Java 文件,還需要織入器。

為了啟用 Spring 對(duì) @AspectJ 方面配置的支持,并保證 Spring 容器中的目標(biāo) Bean 被一個(gè)或多個(gè)方面自動(dòng)增強(qiáng),必須在 Spring 配置文件中配置如下片段:

  1. <?xml version="1.0" encoding="GBK"?>   
  2.  <beans   xmlns="http://www.springframework.org/schema/beans" 
  3.  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  4.  xmlns:aop="http://www.springframework.org/schema/aop" 
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans   
  6. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  7. http://www.springframework.org/schema/aop  
  8. http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">  
  9.  <!-- 啟動(dòng) @AspectJ 支持 -->  
  10.  <aop:aspectj-autoproxy/>   
  11.  </beans> 

當(dāng)然,如果我們希望完全啟動(dòng) Spring 的“零配置”功能,則還需要啟用 Spring 的“零配置”支持,讓 Spring 自動(dòng)搜索指定路徑下 Bean 類(lèi)。

所謂自動(dòng)增強(qiáng),指的是 Spring 會(huì)判斷一個(gè)或多個(gè)方面是否需要對(duì)指定 Bean 進(jìn)行增強(qiáng),并據(jù)此自動(dòng)生成相應(yīng)的代理,從而使得增強(qiáng)處理在合適的時(shí)候被調(diào)用。

如果不打算使用 Spring 的 XML Schema 配置方式,則應(yīng)該在 Spring 配置文件中增加如下片段來(lái)啟用 @AspectJ 支持。

  1. <!-- 啟動(dòng) @AspectJ 支持 -->  
  2. <bean/> 

上面配置文件中的 AnnotationAwareAspectJAutoProxyCreator 是一個(gè) Bean 后處理器(BeanPostProcessor),該 Bean 后處理器將會(huì)為容器中 Bean 生成 AOP 代理,

當(dāng)啟動(dòng)了 @AspectJ 支持后,只要我們?cè)?Spring 容器中配置一個(gè)帶 @Aspect 注釋的 Bean,Spring 將會(huì)自動(dòng)識(shí)別該 Bean,并將該 Bean 作為方面 Bean 處理。

在 Spring 容器中配置方面 Bean(即帶 @Aspect 注釋的 Bean),與配置普通 Bean 沒(méi)有任何區(qū)別,一樣使用 <bean…/> 元素進(jìn)行配置,一樣支持使用依賴(lài)注入來(lái)配置屬性值;如果我們啟動(dòng)了 Spring 的“零配置”特性,一樣可以讓 Spring 自動(dòng)搜索,并裝載指定路徑下的方面 Bean。

使用 @Aspect 標(biāo)注一個(gè) Java 類(lèi),該 Java 類(lèi)將會(huì)作為方面 Bean,如下面代碼片段所示:

  1. // 使用 @Aspect 定義一個(gè)方面類(lèi)  
  2.  @Aspect 
  3.  public class LogAspect   
  4.  {   
  5.  // 定義該類(lèi)的其他內(nèi)容  
  6.  ...   
  7.  } 

方面類(lèi)(用 @Aspect 修飾的類(lèi))和其他類(lèi)一樣可以有方法、屬性定義,還可能包括切入點(diǎn)、增強(qiáng)處理定義。

當(dāng)我們使用 @Aspect 來(lái)修飾一個(gè) Java 類(lèi)之后,Spring 將不會(huì)把該 Bean 當(dāng)成組件 Bean 處理,因此負(fù)責(zé)自動(dòng)增強(qiáng)的后處理 Bean 將會(huì)略過(guò)該 Bean,不會(huì)對(duì)該 Bean 進(jìn)行任何增強(qiáng)處理。

開(kāi)發(fā)時(shí)無(wú)須擔(dān)心使用 @Aspect 定義的方面類(lèi)被增強(qiáng)處理,當(dāng) Spring 容器檢測(cè)到某個(gè) Bean 類(lèi)使用了 @Aspect 標(biāo)注之后,Spring 容器不會(huì)對(duì)該 Bean 類(lèi)進(jìn)行增強(qiáng)。

下面將會(huì)考慮采用 Spring AOP 來(lái)改寫(xiě)前面介紹的例子:

下面例子使用一個(gè)簡(jiǎn)單的 Chinese 類(lèi)來(lái)模擬業(yè)務(wù)邏輯組件:

清單 5.Chinese.java

  1. @Component 
  2.  public class Chinese  
  3.  {  
  4.  // 實(shí)現(xiàn) Person 接口的 sayHello() 方法  
  5.      public String sayHello(String name)  
  6.      {  
  7.     System.out.println("-- 正在執(zhí)行 sayHello 方法 --");  
  8.  // 返回簡(jiǎn)單的字符串  
  9.          return name + " Hello , Spring AOP";  
  10.      }  
  11.  // 定義一個(gè) eat() 方法  
  12.      public void eat(String food)  
  13.      {  
  14.     System.out.println("我正在吃 :"+ food);  
  15.      }  
  16.  } 

提供了上面 Chinese 類(lèi)之后,接下來(lái)假設(shè)同樣需要為上面 Chinese 類(lèi)的每個(gè)方法增加事務(wù)控制、日志記錄,此時(shí)可以考慮使用 Around、AfterReturning 兩種增強(qiáng)處理。

先看 AfterReturning 增強(qiáng)處理代碼。

清單 6.AfterReturningAdviceTest.java

  1. // 定義一個(gè)方面  
  2.  @Aspect 
  3.  public class AfterReturningAdviceTest   
  4.  {   
  5.  // 匹配 org.crazyit.app.service.impl 包下所有類(lèi)的、  
  6.  // 所有方法的執(zhí)行作為切入點(diǎn)  
  7.  @AfterReturning(returning="rvt", pointcut="execution(* org.crazyit.app.service.impl.*.*(..))")  
  8.  public void log(Object rvt)   
  9.  {   
  10.  System.out.println("獲取目標(biāo)方法返回值 :" + rvt);   
  11.  System.out.println("模擬記錄日志功能 ...");   
  12.  }   
  13.  } 

上面 Aspect 類(lèi)使用了 @Aspect 修飾,這樣 Spring 會(huì)將它當(dāng)成一個(gè)方面 Bean 進(jìn)行處理。其中程序中粗體字代碼指定將會(huì)在調(diào)用 org.crazyit.app.service.impl 包下的所有類(lèi)的所有方法之后織入 log(Object rvt) 方法。

再看 Around 增強(qiáng)處理代碼:

清單 7.AfterReturningAdviceTest.java

  1. // 定義一個(gè)方面  
  2.  @Aspect 
  3.  public class AroundAdviceTest  
  4.  {  
  5.  // 匹配 org.crazyit.app.service.impl 包下所有類(lèi)的、  
  6.  // 所有方法的執(zhí)行作為切入點(diǎn)  
  7.  @Around("execution(* org.crazyit.app.service.impl.*.*(..))")  
  8.  public Object processTx(ProceedingJoinPoint jp)  
  9.  throws java.lang.Throwable  
  10.  {  
  11.  System.out.println("執(zhí)行目標(biāo)方法之前,模擬開(kāi)始事務(wù) ...");  
  12.  // 執(zhí)行目標(biāo)方法,并保存目標(biāo)方法執(zhí)行后的返回值  
  13.  Object rvt = jp.proceed(new String[]{"被改變的參數(shù)"});  
  14.  System.out.println("執(zhí)行目標(biāo)方法之后,模擬結(jié)束事務(wù) ...");  
  15.  return rvt + " 新增的內(nèi)容";  
  16.  }  
  17.  } 

與前面的 AfterReturning 增強(qiáng)處理類(lèi)似的,此處同樣使用了 @Aspect 來(lái)修飾前面 Bean,其中粗體字代碼指定在調(diào)用 org.crazyit.app.service.impl 包下的所有類(lèi)的所有方法的“前后(Around)” 織入 processTx(ProceedingJoinPoint jp) 方法

需要指出的是,雖然此處只介紹了 Spring AOP 的 AfterReturning、Around 兩種增強(qiáng)處理,但實(shí)際上 Spring 還支持 Before、After、AfterThrowing 等增強(qiáng)處理,關(guān)于 Spring AOP 編程更多、更細(xì)致的編程細(xì)節(jié),可以參考《輕量級(jí) Java EE 企業(yè)應(yīng)用實(shí)戰(zhàn)》一書(shū)。

本示例采用了 Spring 的零配置來(lái)開(kāi)啟 Spring AOP,因此上面 Chinese 類(lèi)使用了 @Component 修飾,而方面 Bean 則使用了 @Aspect 修飾,方面 Bean 中的 Advice 則分別使用了 @AfterReturning、@Around 修飾。接下來(lái)只要為 Spring 提供如下配置文件即可:

清單 8.bean.xml

  1. <?xml version="1.0" encoding="GBK"?>  
  2.  <beans xmlns="http://www.springframework.org/schema/beans" 
  3.  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  4. xmlns:context="http://www.springframework.org/schema/context" 
  5. xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  7. http://www.springframework.org/schema/context  
  8. http://www.springframework.org/schema/context/spring-context-3.0.xsd  
  9. http://www.springframework.org/schema/aop  
  10. http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">  
  11.    
  12.  <!-- 指定自動(dòng)搜索 Bean 組件、自動(dòng)搜索方面類(lèi) -->  
  13.  <context:component-scan base-package="org.crazyit.app.service  
  14.  ,org.crazyit.app.advice">  
  15.  <context:include-filter type="annotation" 
  16.  expression="org.aspectj.lang.annotation.Aspect"/>  
  17.  </context:component-scan>  
  18.  <!-- 啟動(dòng) @AspectJ 支持 -->  
  19.  <aop:aspectj-autoproxy/>  
  20.  </beans> 

接下來(lái)按傳統(tǒng)方式來(lái)獲取 Spring 容器中 chinese Bean、并調(diào)用該 Bean 的兩個(gè)方法,程序代碼如下:

清單 9.BeanTest.java

  1. public class BeanTest   
  2.  {   
  3.  public static void main(String[] args)   
  4.  {   
  5.  // 創(chuàng)建 Spring 容器  
  6.  ApplicationContext ctx = new 
  7.  ClassPathXmlApplicationContext("bean.xml");   
  8.  Chinese p = ctx.getBean("chinese" ,Chinese.class);   
  9.  System.out.println(p.sayHello("張三"));   
  10.  p.eat("西瓜");   
  11.  }   
  12.  } 

從上面開(kāi)發(fā)過(guò)程可以看出,對(duì)于 Spring AOP 而言,開(kāi)發(fā)者提供的業(yè)務(wù)組件、方面 Bean 并沒(méi)有任何特別的地方。只是方面 Bean 需要使用 @Aspect 修飾即可。程序不需要使用特別的編譯器、織入器進(jìn)行處理。

運(yùn)行上面程序,將可以看到如下執(zhí)行結(jié)果:

執(zhí)行目標(biāo)方法之前,模擬開(kāi)始事務(wù) …

– 正在執(zhí)行 sayHello 方法 –

執(zhí)行目標(biāo)方法之后,模擬結(jié)束事務(wù) …

獲取目標(biāo)方法返回值 : 被改變的參數(shù) Hello , Spring AOP 新增的內(nèi)容

模擬記錄日志功能 …

被改變的參數(shù) Hello , Spring AOP 新增的內(nèi)容

執(zhí)行目標(biāo)方法之前,模擬開(kāi)始事務(wù) …

我正在吃 : 被改變的參數(shù)

執(zhí)行目標(biāo)方法之后,模擬結(jié)束事務(wù) …

獲取目標(biāo)方法返回值 :null 新增的內(nèi)容

模擬記錄日志功能 …

雖然程序是在調(diào)用 Chinese 對(duì)象的 sayHello、eat 兩個(gè)方法,但從上面運(yùn)行結(jié)果不難看出:實(shí)際執(zhí)行的絕對(duì)不是 Chinese 對(duì)象的方法,而是 AOP 代理的方法。也就是說(shuō),Spring AOP 同樣為 Chinese 類(lèi)生成了 AOP 代理類(lèi)。這一點(diǎn)可通過(guò)在程序中增加如下代碼看出:System.out.println(p.getClass());

上面代碼可以輸出 p 變量所引用對(duì)象的實(shí)現(xiàn)類(lèi),再次執(zhí)行程序?qū)⒖梢钥吹缴厦娲a產(chǎn)生 class org.crazyit.app.service.impl.Chinese$$EnhancerByCGLIB$$290441d2 的輸出,這才是 p 變量所引用的對(duì)象的實(shí)現(xiàn)類(lèi),這個(gè)類(lèi)也就是 Spring AOP 動(dòng)態(tài)生成的 AOP 代理類(lèi)。從 AOP 代理類(lèi)的類(lèi)名可以看出,AOP 代理類(lèi)是由 CGLIB 來(lái)生成的。

如果將上面程序程序稍作修改:只要讓上面業(yè)務(wù)邏輯類(lèi) Chinese 類(lèi)實(shí)現(xiàn)一個(gè)任意接口——這種做法更符合 Spring 所倡導(dǎo)的“面向接口編程”的原則。假設(shè)程序?yàn)?Chinese 類(lèi)提供如下 Person 接口,并讓 Chinese 類(lèi)實(shí)現(xiàn)該接口:

清單 10.Person.java

  1. public interface Person  
  2.  {  
  3.  String sayHello(String name);  
  4.  void eat(String food);  
  5.  } 

接下來(lái)讓 BeanTest 類(lèi)面向 Person 接口、而不是 Chinese 類(lèi)編程。即將 BeanTest 類(lèi)改為如下形式:

清單 11.BeanTest.java

  1. public class BeanTest   
  2.  {   
  3.  public static void main(String[] args)   
  4.  {   
  5.  // 創(chuàng)建 Spring 容器  
  6.  ApplicationContext ctx = new 
  7.  ClassPathXmlApplicationContext("bean.xml");   
  8.  Person p = ctx.getBean("chinese" ,Person.class);  
  9.  System.out.println(p.sayHello("張三"));   
  10.  p.eat("西瓜");   
  11.  System.out.println(p.getClass());   
  12.  }   
  13.  } 

原來(lái)的程序是將面向 Chinese 類(lèi)編程,現(xiàn)在將該程序改為面向 Person 接口編程,再次運(yùn)行該程序,程序運(yùn)行結(jié)果沒(méi)有發(fā)生改變。只是 System.out.println(p.getClass()); 將會(huì)輸出 class $Proxy7,這說(shuō)明此時(shí)的 AOP 代理并不是由 CGLIB 生成的,而是由 JDK 動(dòng)態(tài)代理生成的。

Spring AOP 框架對(duì) AOP 代理類(lèi)的處理原則是:如果目標(biāo)對(duì)象的實(shí)現(xiàn)類(lèi)實(shí)現(xiàn)了接口,Spring AOP 將會(huì)采用 JDK 動(dòng)態(tài)代理來(lái)生成 AOP 代理類(lèi);如果目標(biāo)對(duì)象的實(shí)現(xiàn)類(lèi)沒(méi)有實(shí)現(xiàn)接口,Spring AOP 將會(huì)采用 CGLIB 來(lái)生成 AOP 代理類(lèi)——不過(guò)這個(gè)選擇過(guò)程對(duì)開(kāi)發(fā)者完全透明、開(kāi)發(fā)者也無(wú)需關(guān)心。

Spring AOP 會(huì)動(dòng)態(tài)選擇使用 JDK 動(dòng)態(tài)代理、CGLIB 來(lái)生成 AOP 代理,如果目標(biāo)類(lèi)實(shí)現(xiàn)了接口,Spring AOP 則無(wú)需 CGLIB 的支持,直接使用 JDK 提供的 Proxy 和 InvocationHandler 來(lái)生成 AOP 代理即可。關(guān)于如何 Proxy 和 InvocationHandler 來(lái)生成動(dòng)態(tài)代理不在本文介紹范圍之內(nèi),如果讀者對(duì) Proxy 和 InvocationHandler 的用法感興趣則可自行參考 Java API 文檔或《瘋狂 Java 講義》。

Spring AOP 原理剖析

通過(guò)前面介紹可以知道:AOP 代理其實(shí)是由 AOP 框架動(dòng)態(tài)生成的一個(gè)對(duì)象,該對(duì)象可作為目標(biāo)對(duì)象使用。AOP 代理包含了目標(biāo)對(duì)象的全部方法,但 AOP 代理中的方法與目標(biāo)對(duì)象的方法存在差異:AOP 方法在特定切入點(diǎn)添加了增強(qiáng)處理,并回調(diào)了目標(biāo)對(duì)象的方法.

AOP 代理所包含的方法與目標(biāo)對(duì)象的方法示意圖如圖 3 所示。

圖 3.AOP 代理的方法與目標(biāo)對(duì)象的方法

Spring AOP 實(shí)現(xiàn)原理與 CGLIB 應(yīng)用

Spring 的 AOP 代理由 Spring 的 IoC 容器負(fù)責(zé)生成、管理,其依賴(lài)關(guān)系也由 IoC 容器負(fù)責(zé)管理。因此,AOP 代理可以直接使用容器中的其他 Bean 實(shí)例作為目標(biāo),這種關(guān)系可由 IoC 容器的依賴(lài)注入提供。

縱觀 AOP 編程,其中需要程序員參與的只有 3 個(gè)部分:

● 定義普通業(yè)務(wù)組件。

● 定義切入點(diǎn),一個(gè)切入點(diǎn)可能橫切多個(gè)業(yè)務(wù)組件。

● 定義增強(qiáng)處理,增強(qiáng)處理就是在 AOP 框架為普通業(yè)務(wù)組件織入的處理動(dòng)作。

上面 3 個(gè)部分的***個(gè)部分是最平常不過(guò)的事情,無(wú)須額外說(shuō)明。那么進(jìn)行 AOP 編程的關(guān)鍵就是定義切入點(diǎn)和定義增強(qiáng)處理。一旦定義了合適的切入點(diǎn)和增強(qiáng)處理,AOP 框架將會(huì)自動(dòng)生成 AOP 代理,而 AOP 代理的方法大致有如下公式:

代理對(duì)象的方法 = 增強(qiáng)處理 + 被代理對(duì)象的方法

在上面這個(gè)業(yè)務(wù)定義中,不難發(fā)現(xiàn) Spring AOP 的實(shí)現(xiàn)原理其實(shí)很簡(jiǎn)單:AOP 框架負(fù)責(zé)動(dòng)態(tài)地生成 AOP 代理類(lèi),這個(gè)代理類(lèi)的方法則由 Advice 和回調(diào)目標(biāo)對(duì)象的方法所組成。

對(duì)于前面提到的圖 2 所示的軟件調(diào)用結(jié)構(gòu):當(dāng)方法 1、方法 2、方法 3 ……都需要去調(diào)用某個(gè)具有“橫切”性質(zhì)的方法時(shí),傳統(tǒng)的做法是程序員去手動(dòng)修改方法 1、方法 2、方法 3 ……、通過(guò)代碼來(lái)調(diào)用這個(gè)具有“橫切”性質(zhì)的方法,但這種做法的可擴(kuò)展性不好,因?yàn)槊看味家拇a。

于是 AOP 框架出現(xiàn)了,AOP 框架則可以“動(dòng)態(tài)的”生成一個(gè)新的代理類(lèi),而這個(gè)代理類(lèi)所包含的方法 1、方法 2、方法 3 ……也增加了調(diào)用這個(gè)具有“橫切”性質(zhì)的方法——但這種調(diào)用由 AOP 框架自動(dòng)生成的代理類(lèi)來(lái)負(fù)責(zé),因此具有了極好的擴(kuò)展性。程序員無(wú)需手動(dòng)修改方法 1、方法 2、方法 3 的代碼,程序員只要定義切入點(diǎn)即可—— AOP 框架所生成的 AOP 代理類(lèi)中包含了新的方法 1、訪法 2、方法 3,而 AOP 框架會(huì)根據(jù)切入點(diǎn)來(lái)決定是否要在方法 1、方法 2、方法 3 中回調(diào)具有“橫切”性質(zhì)的方法。

簡(jiǎn)而言之:AOP 原理的奧妙就在于動(dòng)態(tài)地生成了代理類(lèi),這個(gè)代理類(lèi)實(shí)現(xiàn)了圖 2 的調(diào)用——這種調(diào)用無(wú)需程序員修改代碼。接下來(lái)介紹的 CGLIB 就是一個(gè)代理生成庫(kù),下面介紹如何使用 CGLIB 來(lái)生成代理類(lèi)。

使用 CGLIB 生成代理類(lèi)

CGLIB(Code Generation Library),簡(jiǎn)單來(lái)說(shuō),就是一個(gè)代碼生成類(lèi)庫(kù)。它可以在運(yùn)行時(shí)候動(dòng)態(tài)是生成某個(gè)類(lèi)的子類(lèi)。

此處使用前面定義的 Chinese 類(lèi),現(xiàn)在改為直接使用 CGLIB 來(lái)生成代理,這個(gè)代理類(lèi)同樣可以實(shí)現(xiàn) Spring AOP 代理所達(dá)到的效果。

下面先為 CGLIB 提供一個(gè)攔截器實(shí)現(xiàn)類(lèi):

清單 12.AroundAdvice.java

  1. public class AroundAdvice implements MethodInterceptor   
  2.  {   
  3.  public Object intercept(Object target, Method method   
  4.  , Object[] args, MethodProxy proxy)   
  5.  throws java.lang.Throwable   
  6.  {   
  7.  System.out.println("執(zhí)行目標(biāo)方法之前,模擬開(kāi)始事務(wù) ...");   
  8.  // 執(zhí)行目標(biāo)方法,并保存目標(biāo)方法執(zhí)行后的返回值  
  9.  Object rvt = proxy.invokeSuper(target, new String[]{"被改變的參數(shù)"});   
  10.  System.out.println("執(zhí)行目標(biāo)方法之后,模擬結(jié)束事務(wù) ...");   
  11.  return rvt + " 新增的內(nèi)容";   
  12.  }   
  13.  } 

上面這個(gè) AroundAdvice.java 的作用就像前面介紹的 Around Advice,它可以在調(diào)用目標(biāo)方法之前、調(diào)用目標(biāo)方法之后織入增強(qiáng)處理。

接下來(lái)程序提供一個(gè) ChineseProxyFactory 類(lèi),這個(gè) ChineseProxyFactory 類(lèi)會(huì)通過(guò) CGLIB 來(lái)為 Chinese 生成代理類(lèi):

清單 13.ChineseProxyFactory.java

  1. public class ChineseProxyFactory   
  2. {   
  3. public static Chinese getAuthInstance()   
  4. {   
  5. Enhancer en = new Enhancer();   
  6. // 設(shè)置要代理的目標(biāo)類(lèi)  
  7. en.setSuperclass(Chinese.class);  
  8. // 設(shè)置要代理的攔截器  
  9. en.setCallback(new AroundAdvice());  
  10. // 生成代理類(lèi)的實(shí)例   
  11. return (Chinese)en.create();  
  12. }   

上面粗體字代碼就是使用 CGLIB 的 Enhancer 生成代理對(duì)象的關(guān)鍵代碼,此時(shí)的 Enhancer 將以 Chinese 類(lèi)作為目標(biāo)類(lèi),以 AroundAdvice 對(duì)象作為“Advice”,程序?qū)?huì)生成一個(gè) Chinese 的子類(lèi),這個(gè)子類(lèi)就是 CGLIB 生成代理類(lèi),它可作為 Chinese 對(duì)象使用,但它增強(qiáng)了 Chinese 類(lèi)的方法。

測(cè)試 Chinese 代理類(lèi)的主程序如下:

清單 14.Main.java

  1. public class Main   
  2.  {   
  3.  public static void main(String[] args)   
  4.  {   
  5.  Chinese chin = ChineseProxyFactory.getAuthInstance();   
  6.  System.out.println(chin.sayHello("孫悟空"));   
  7.  chin.eat("西瓜");   
  8.  System.out.println(chin.getClass());   
  9.  }   
  10.  } 

運(yùn)行上面主程序,看到如下輸出結(jié)果:

執(zhí)行目標(biāo)方法之前,模擬開(kāi)始事務(wù) …

– 正在執(zhí)行 sayHello 方法 –

執(zhí)行目標(biāo)方法之后,模擬結(jié)束事務(wù) …

被改變的參數(shù) Hello , CGLIB 新增的內(nèi)容

執(zhí)行目標(biāo)方法之前,模擬開(kāi)始事務(wù) …

我正在吃 : 被改變的參數(shù)

執(zhí)行目標(biāo)方法之后,模擬結(jié)束事務(wù) …

class lee.Chinese$$EnhancerByCGLIB$$4bd097d9

從上面輸出結(jié)果來(lái)看,CGLIB 生成的代理完全可以作為 Chinese 對(duì)象來(lái)使用,而且 CGLIB 代理對(duì)象的 sayHello()、eat() 兩個(gè)方法已經(jīng)增加了事務(wù)控制(只是模擬),這個(gè) CGLIB 代理其實(shí)就是 Spring AOP 所生成的 AOP 代理。

通過(guò)程序***的輸出,不難發(fā)現(xiàn)這個(gè)代理對(duì)象的實(shí)現(xiàn)類(lèi)是 lee.Chinese$$EnhancerByCGLIB$$4bd097d9,這就是 CGLIB 所生成的代理類(lèi),這個(gè)代理類(lèi)的格式與前面 Spring AOP 所生成的代理類(lèi)的格式完全相同。

這就是 Spring AOP 的根本所在:Spring AOP 就是通過(guò) CGLIB 來(lái)動(dòng)態(tài)地生成代理對(duì)象,這個(gè)代理對(duì)象就是所謂的 AOP 代理,而 AOP 代理的方法則通過(guò)在目標(biāo)對(duì)象的切入點(diǎn)動(dòng)態(tài)地織入增強(qiáng)處理,從而完成了對(duì)目標(biāo)方法的增強(qiáng)。

小結(jié)

AOP 廣泛應(yīng)用于處理一些具有橫切性質(zhì)的系統(tǒng)級(jí)服務(wù),AOP 的出現(xiàn)是對(duì) OOP 的良好補(bǔ)充,它使得開(kāi)發(fā)者能用更優(yōu)雅的方式處理具有橫切性質(zhì)的服務(wù)。不管是那種 AOP 實(shí)現(xiàn),不論是 AspectJ、還是 Spring AOP,它們都需要?jiǎng)討B(tài)地生成一個(gè) AOP 代理類(lèi),區(qū)別只是生成 AOP 代理類(lèi)的時(shí)機(jī)不同:AspectJ 采用編譯時(shí)生成 AOP 代理類(lèi),因此具有更好的性能,但需要使用特定的編譯器進(jìn)行處理;而 Spring AOP 則采用運(yùn)行時(shí)生成 AOP 代理類(lèi),因此無(wú)需使用特定編譯器進(jìn)行處理。由于 Spring AOP 需要在每次運(yùn)行時(shí)生成 AOP 代理,因此性能略差一些。

原文鏈接:http://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/index.html?ca=drs-

【編輯推薦】

  1. Java環(huán)境變量Classpath
  2. 8種常見(jiàn)的Java不規(guī)范代碼
  3. Java多線程發(fā)展簡(jiǎn)史
  4. Java 動(dòng)態(tài)代理(Proxy)
  5. 東方程序員怎么看西方程序員_開(kāi)發(fā)技術(shù)周刊第069期
責(zé)任編輯:張偉 來(lái)源: IBM Developerworks
相關(guān)推薦

2012-09-28 10:20:14

IBMdw

2013-05-22 15:31:07

AOP的CGlib實(shí)現(xiàn)

2022-07-01 09:39:58

SpringAOPIOC

2024-11-04 16:29:19

2018-11-16 15:35:10

Spring事務(wù)Java

2024-12-24 14:01:10

2022-09-01 10:40:29

SpringAOPJDK

2018-10-25 16:20:23

JavaSpring AOPSpringMVC

2024-09-05 09:35:58

CGLIBSpring動(dòng)態(tài)代理

2023-05-04 00:06:40

2021-07-14 11:07:56

AOPJDKCglib

2022-02-08 17:07:54

Spring BooSpring Aop日志記錄

2011-09-15 10:15:30

Spring

2009-06-19 13:28:30

Spring AOPSpring 2.0

2022-06-07 07:58:45

SpringSpring AOP

2023-11-27 08:17:05

SpringJava

2025-02-27 00:32:35

2019-11-29 16:21:22

Spring框架集成

2009-06-22 10:41:34

Spring.AOP

2022-02-17 13:39:09

AOP接口方式
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

欧美俄罗斯性视频| 欧美另类一区二区三区| 欧美激情精品久久久久久大尺度| 日本黄色三级网站| 无码人妻丰满熟妇精品| 野花视频免费在线观看| 日韩精品成人av| 欧美aaaaa级| 国产成人综合在线| 精品国内亚洲在观看18黄| 国产无套内射久久久国产| 亚洲美女性生活| 欧美国产精品| 欧美一区午夜视频在线观看| 亚洲精品在线观看免费| 中文字幕天堂在线| 欧美丝袜一区| 欧美专区日韩专区| 亚欧精品在线| 日韩xxx视频| 91日韩视频| 在线电影欧美成精品| 黄色大片在线免费看| 欧美一区二区三区黄片| 国产精品va| 精品国产乱子伦一区| 久草视频这里只有精品| www.精品久久| 日韩午夜电影| 日韩精品久久久久| 91精品91久久久中77777老牛| 天堂网在线资源| 国产欧美二区| 亚洲香蕉伊综合在人在线视看| 免费观看成人网| 97人人在线| 国产真实乱子伦精品视频| 日韩在线精品一区| 在线免费黄色小视频| 精灵使的剑舞无删减版在线观看| 不卡视频在线看| 日本精品久久久久久久| 免费看的黄色录像| 久久爱www.| 精品久久久久久中文字幕一区奶水 | 欧美一区二区成人| 亚洲视频第二页| 黄色av电影在线观看| 国产成人午夜精品5599 | 成人偷拍自拍| 欧美视频免费在线观看| 婷婷四房综合激情五月| 欧美日韩国产综合视频| 久久精品久久精品| 九九热这里只有精品6| bl动漫在线观看| 欧美电影h版| 成人欧美一区二区三区白人| av免费观看久久| 欧美男人亚洲天堂| 亚洲精品中文字幕乱码| 日韩精品在线观| 91pony九色| 女厕盗摄一区二区三区| 亚洲色欲色欲www| 鲁丝一区鲁丝二区鲁丝三区| 91精品在线视频观看| 亚洲精一区二区三区| 在线观看亚洲视频| 少妇精品无码一区二区三区| 国产美女撒尿一区二区| 欧美老人xxxx18| 特黄视频免费观看| 欧美在线在线| 欧美日韩三级视频| 北条麻妃在线视频观看| 色噜噜狠狠狠综合欧洲色8| 久久久精品国产免费观看同学| 91精品视频一区| 极品国产91在线网站| 麻豆九一精品爱看视频在线观看免费| 欧美超级乱淫片喷水| www.中文字幕av| av自拍一区| 亚洲精品自产拍| 在线观看日本黄色| 欧美日韩亚洲一区| 日韩一级黄色av| 精品无码国产污污污免费网站| 国产毛片久久久| 亚洲一级一级97网| 久草网在线观看| 亚洲澳门在线| 中文字幕日韩在线视频| 日韩中文字幕电影| 国产精品久久久久久久久妇女| 亚洲精品在线观看www| 天天舔天天操天天干| 亚洲动漫在线观看| 日韩精品在线观| 登山的目的在线| 精品日本12videosex| 亚洲少妇中文在线| 日本少妇色视频| 久久精品国产亚洲blacked| 日韩欧美国产一区二区在线播放| 向日葵污视频在线观看| 第一区第二区在线| 日韩小视频在线| 91国产丝袜播放在线| 欧美日韩加勒比精品一区| 欧美小视频在线| 青青草精品视频在线观看| а√在线天堂官网| 亚洲自拍偷拍欧美| 91.com在线| 欧美激情福利| 欧美日韩国产电影| 少妇精品一区二区三区| 奇米亚洲欧美| 中文在线不卡视频| 97超碰人人干| 国产成人av自拍| 国产一区在线免费| 爽爽视频在线观看| 免费不卡av| 日本乱码高清不卡字幕| 麻豆传传媒久久久爱| 国产日韩另类视频一区| 欧洲一区二区av| 91丝袜在线观看| 黄色亚洲在线| 亚洲影院高清在线| 黄色三级网站在线观看| 亚洲另类在线一区| 日韩精品在线中文字幕| 蜜臀久久精品| 亚洲国产古装精品网站| 中国美女乱淫免费看视频| 黄色免费成人| 99在线观看| 天堂亚洲精品| 国产成人高清在线| 久久婷婷国产麻豆91天堂| 在线观看免费视频国产| 任你躁在线精品免费| 色综合老司机第九色激情| 国产免费一区二区三区最新不卡| 国产呦萝稀缺另类资源| 一区二区精品视频| 黄色手机在线视频| 疯狂欧洲av久久成人av电影| 精品国产伦一区二区三区观看体验| 老司机成人免费视频| 亚洲黄色av| 成人看片视频| 国产视频网站在线| 亚洲激情图片qvod| 国产免费成人在线| 亚洲人成网www| 秋霞午夜一区二区| 国产福利资源在线| 久久久久久久久久久黄色| 国产成人a亚洲精v品无码| 亚洲小说图片| 国产精品视频1区| 深夜福利视频网站| 亚洲高清免费一级二级三级| 色悠悠久久综合网| 99久久亚洲精品蜜臀| 91在线视频免费| 污视频在线看网站| 亚洲精品www久久久久久广东| av资源在线免费观看| 国产精品色网| 日本一区网站| 黄色视屏在线免费观看| 亚洲毛片在线观看| 久久久久无码国产精品| 久久精品理论片| 国产一级大片免费看| 欧美男女视频| 久久777国产线看观看精品| 天天干天天草天天射| 欧美在线短视频| ass精品国模裸体欣赏pics| 天堂资源在线中文精品| 国产日韩欧美一区二区| 日韩av福利| 久久在线免费观看视频| 少妇一级淫片免费看| 欧美曰成人黄网| 精品无码m3u8在线观看| 国产午夜亚洲精品不卡| 国产亚洲精品网站| 91视频精品| 久久综合久久久| 麻豆一区在线| 国产www精品| 全色精品综合影院| 精品国产1区2区| 国产精品综合激情| av一本久道久久综合久久鬼色| 欧美黄色免费网址| jlzzjlzz亚洲女人| 国产精品久久久久av福利动漫| √天堂8在线网| 日韩免费高清av| 销魂美女一区二区| 亚洲成人综合视频| 男人在线观看视频| 欧美经典一区二区| 视色视频在线观看| 99xxxx成人网| 国产一级片91| 999国产精品一区| 国产精品视频一区二区高潮| 不卡专区在线| 九九热这里只有在线精品视| 午夜毛片在线| 亚洲视频在线观看| 午夜性色福利视频| 色偷偷成人一区二区三区91| 黄色三级生活片| www.性欧美| 国产精品熟妇一区二区三区四区 | 久热精品在线观看| 国产精品久久久久aaaa| 五月花丁香婷婷| 女同性一区二区三区人了人一 | 1卡2卡3卡精品视频| 青青草原av在线| 久久精品一区中文字幕| 国产视频网站在线| 亚洲深夜福利在线| 国产中文在线视频| 7777精品伊人久久久大香线蕉经典版下载| 日韩欧美123区| 中文字幕中文字幕中文字幕亚洲无线 | 一级一片免费看| 欧美日韩国内自拍| 影音先锋亚洲天堂| 欧美日韩国产专区| 国产又大又黄又粗| 中文字幕一区二区日韩精品绯色| 91成人破解版| 国产亚洲精品精华液| 中国女人特级毛片| 国产精品一级片| 黄色片久久久久| 亚洲欧美日本视频在线观看| 亚洲永久一区二区三区在线| 欧美色蜜桃97| 在线看无码的免费网站| 久久精品福利| 精品一区日韩成人| 91久久青草| 啪一啪鲁一鲁2019在线视频| 午夜裸体女人视频网站在线观看| 久久国产一区二区三区| 精品视频在线一区二区| 美女视频久久黄| 激情影院在线| 亚洲天堂av综合网| 在线视频自拍| 欧美精品在线免费观看| 丁香花在线观看完整版电影| 国产91精品久| 丝袜在线视频| 国内精品美女av在线播放| 日韩精品黄色| 欧美丰满老妇厨房牲生活| 嗯啊主人调教在线播放视频| 日本午夜人人精品| 欧美91在线|欧美| 成人国产一区二区| 中文字幕中文字幕精品| 亚洲综合网中心| 国产精品成人一区二区网站软件| 亚洲熟妇av一区二区三区漫画| 日本麻豆一区二区三区视频| 免费在线激情视频| 久久草av在线| 国产九九在线视频| 国产福利一区二区| 人妻少妇精品视频一区二区三区| 国产精品无码永久免费888| 大地资源二中文在线影视观看 | 少妇免费毛片久久久久久久久| 91精品国产乱码久久久久久久| 国产情侣第一页| 日韩电影一区二区三区四区| 九九久久九九久久| 亚洲一区日韩在线| 午夜一区二区视频| 99国产精品国产精品毛片| 51自拍视频在线观看| 99国产一区二区三精品乱码| 久久久久久久麻豆| 色拍拍在线精品视频8848| 99精品在线看| 亚洲一品av免费观看| a级片在线免费观看| 91禁外国网站| 色资源二区在线视频| 成人www视频在线观看| 91精品网站在线观看| 国外成人免费视频| 亚洲一级毛片| 无需播放器的av| 99精品久久只有精品| 成人在线观看小视频| 色天天综合久久久久综合片| 亚洲卡一卡二卡三| 日韩电影免费观看中文字幕| 黄网页在线观看| 国产精品久久久久久亚洲影视 | 国产午夜精品美女视频明星a级| 四虎亚洲精品| 亚洲精品免费av| 91蜜臀精品国产自偷在线| 久久综合久久色| 91色在线porny| 免费黄色片网站| 午夜精品成人在线视频| 69成人免费视频| 亚洲成成品网站| 免费男女羞羞的视频网站在线观看| 成人激情免费在线| 日韩精品二区| 久久亚洲a v| 精品无人码麻豆乱码1区2区| 超碰中文字幕在线观看| 国产精品久久久久久久久搜平片 | 久久高清内射无套| 欧美高清视频不卡网| 日韩黄色影院| 成人网页在线免费观看| 一区二区三区四区电影| 欧美又黄又嫩大片a级| 成人av先锋影音| 精品一区二区三区人妻| 精品国产乱码久久久久久牛牛 | 99精品在线| 色噜噜狠狠一区二区三区狼国成人| 国产精品乱码一区二区三区软件| 久久久久久福利| 日韩免费高清视频| 538在线观看| 久久99九九| 91精品蜜臀一区二区三区在线| 日本黄色福利视频| √…a在线天堂一区| 国产三级在线观看视频| 欧美日韩国产二区| 黄色成人美女网站| 成人中文字幕在线播放| 久久久久亚洲蜜桃| 中文字幕一区二区三区四区免费看| 精品久久久久久久人人人人传媒| 大胆av不用播放器在线播放| 久久全球大尺度高清视频| 国产ts一区| 蜜臀av午夜一区二区三区| 中日韩免费视频中文字幕| 国产精品一区二区三区在线免费观看| 精品国产一区二区三区av性色| av资源一区| 日本一区二区不卡高清更新| 亚洲激情另类| 成都免费高清电影| 欧美精品色综合| a级大胆欧美人体大胆666| 日韩aⅴ视频一区二区三区| 极品少妇xxxx精品少妇偷拍 | 午夜亚洲福利| 国产福利短视频| 欧美日韩不卡一区二区| 青草视频在线免费直播| 欧美日韩亚洲在线 | 97精品视频在线播放| 精品亚洲二区| 成人av一级片| 国产精品久久久久久久久久久免费看 | 亚洲人成电影在线播放| www.欧美视频| 国产免费毛卡片| 最新高清无码专区| 五月婷婷在线播放| 成人免费网站在线| 亚洲影音一区| 永久看片925tv| 制服丝袜在线91| 国产精品蜜芽在线观看| 中文字幕av日韩精品| 91亚洲男人天堂| 国产超碰人人模人人爽人人添| 热久久这里只有精品| 午夜激情一区| av资源在线免费观看|