Spring AOP,AspectJ,CGLIB 有點暈

來源:http://t.cn/E7KtEEy

AOP(Aspect Orient Programming),作爲面向對象編程的一種補充,廣泛應用於處理一些具有橫切性質的系統級服務,如事務管理、安全檢查、緩存、對象池管理等。AOP 實現的關鍵就在於 AOP 框架自動創建的 AOP 代理,AOP 代理則可分爲靜態代理和動態代理兩大類,其中靜態代理是指使用 AOP 框架提供的命令進行編譯,從而在編譯階段就可生成 AOP 代理類,因此也稱爲編譯時增強;而動態代理則在運行時藉助於 JDK 動態代理、CGLIB 等在內存中“臨時”生成 AOP 動態代理類,因此也被稱爲運行時增強。

1. 先說說AspectJ

在今天之前,我還以爲AspectJ 是Spring的一部分,因爲我們談到Spring AOP一般都會提到AspectJ。原來AspectJ是一套獨立的面向切面編程的解決方案。下面我們拋開Spring,單純的看看AspectJ。

1.1 AspectJ 安裝

AspectJ 下載地址(http://www.eclipse.org/aspectj/downloads.php)。

下載AspectJ jar包,然後雙擊安裝。安裝好的目錄結構爲:

  • bin:存放了 aj、aj5、ajc、ajdoc、ajbrowser 等命令,其中 ajc 命令最常用,它的作用類似於 javac
  • doc:存放了 AspectJ 的使用說明、參考手冊、API 文檔等文檔
  • lib:該路徑下的 4 個 JAR 文件是 AspectJ 的核心類庫

1.2 AspectJ HelloWorld 實現

// 業務組件  SayHelloService

package com.ywsc.fenfenzhong.aspectj.learn;
public class SayHelloService {
    public void say(){
        System.out.print("Hello  AspectJ");
    }
}

需要來了,在需要在調用say()方法之後,需要記錄日誌。那就是通過AspectJ的後置增強吧。

LogAspect 日誌記錄組件,實現對com.ywsc.fenfenzhong.aspectj.learn.SayHelloService 後置增強

package com.ywsc.fenfenzhong.aspectj.learn;
public aspect LogAspect {
    pointcut logPointcut():execution(void SayHelloService.say());
    after():logPointcut(){
         System.out.println("記錄日誌 ...");
    }
}

編譯 SayHelloService:

執行命令   ajc -d . SayHelloService.java LogAspect.java
生成 SayHelloService.class
執行命令    java SayHelloService
輸出  Hello AspectJ  記錄日誌
  • ajc.exe 可以理解爲 javac.exe 命令,都用於編譯 Java 程序,區別是 ajc.exe 命令可識別 AspectJ 的語法;我們可以將 ajc.exe 當成一個增強版的 javac.exe 命令.執行ajc命令後的 SayHelloService.class 文件不是由原來的 SayHelloService.java 文件編譯得到的,該 SayHelloService.class 裏新增了打印日誌的內容——這表明 AspectJ 在編譯時“自動”編譯得到了一個新類,這個新類增強了原有的 SayHelloService.java 類的功能,因此 AspectJ 通常被稱爲編譯時增強的 AOP 框架。

與 AspectJ 相對的還有另外一種 AOP 框架,它不需要在編譯時對目標類進行增強,而是運行時生成目標類的代理類,該代理類要麼與目標類實現相同的接口,要麼是目標類的子類——總之,代理類的實例可作爲目標類的實例來使用。一般來說,編譯時增強的 AOP 框架在性能上更有優勢——因爲運行時動態增強的 AOP 框架需要每次運行時都進行動態增強。

2. 再談 Spring AOP

Spring AOP也是對目標類增強,生成代理類。但是與AspectJ的最大區別在於---Spring AOP的運行時增強,而AspectJ是編譯時增強。

曾經以爲AspectJ是Spring AOP一部分,是因爲Spring AOP使用了AspectJ的Annotation。使用了Aspect來定義切面,使用Pointcut來定義切入點,使用Advice來定義增強處理。雖然使用了Aspect的Annotation,但是並沒有使用它的編譯器和織入器。其實現原理是JDK 動態代理,在運行時生成代理類。

爲了啓用 Spring 對 @AspectJ 方面配置的支持,並保證 Spring 容器中的目標 Bean 被一個或多個方面自動增強,必須在 Spring 配置文件中添加如下配置

<aop:aspectj-autoproxy/>

當啓動了 @AspectJ 支持後,在 Spring 容器中配置一個帶 @Aspect 註釋的 Bean,Spring 將會自動識別該 Bean,並將該 Bean 作爲方面 Bean 處理。方面Bean與普通 Bean 沒有任何區別,一樣使用 <bean.../> 元素進行配置,一樣支持使用依賴注入來配置屬性值。

2.1 Spring AOP HelloWorld 實現

使用Spring AOP的改寫 Hello World的例子。

// 業務組件  SayHelloService

package com.ywsc.fenfenzhong.aspectj.learn;
import org.springframework.stereotype.Component;
@Component
public class SayHelloService {
    public void say(){
        System.out.print("Hello  AspectJ");
    }
}

做後置增強的日誌處理。

package com.ywsc.fenfenzhong.aspectj.learn;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect {
     @After("execution(* com.ywsc.fenfenzhong.aspectj.learn.SayHelloService.*(..))")
     public void log(){
         System.out.println("記錄日誌 ...");
     }
}
package com.ywsc.fenfenzhong.mongodb;
import com.ywsc.fenfenzhong.aspectj.learn.SayHelloService;
public class TestCase {
    public static void main(String[] args) {
        SayHelloService sayHelloService = ApplicationUtil.getContext().getBean(SayHelloService.class);
        sayHelloService.say();
    }
}

輸出結果

Hello  AspectJ

記錄日誌…

2.2 Hello World 後的總結

AOP 代理 = 原來的業務類+增強處理。

這個生成AOP 代理由 Spring 的 IoC 容器負責生成。也由 IoC 容器負責管理。因此,AOP 代理可以直接使用容器中的其他 Bean 實例作爲目標,這種關係可由 IoC 容器的依賴注入提供。回顧Hello World的例子,其中程序員參與的只有 3 個部分:

  • 定義普通業務組件。
  • 定義切入點,一個切入點可能橫切多個業務組件。
  • 定義增強處理,增強處理就是在 AOP 框架爲普通業務組件織入的處理動作。

3. 最後說說CGLIB

CGLIB(Code Generation Library)它是一個代碼生成類庫。它可以在運行時候動態是生成某個類的子類。代理模式爲要訪問的目標對象提供了一種途徑,當訪問對象時,它引入了一個間接的層。JDK自從1.3版本開始,就引入了動態代理,並且經常被用來動態地創建代理。JDK的動態代理用起來非常簡單,唯一限制便是使用動態代理的對象必須實現一個或多個接口。而CGLIB缺不必有此限制。要想Spring AOP 通過CGLIB生成代理,只需要在Spring 的配置文件引入

<aop:aspectj-autoproxy proxy-target-class="true"/>

CGLIB包的底層是通過使用一個小而快的字節碼處理框架ASM(Java字節碼操控框架),來轉換字節碼並生成新的類。由於沒有了解過class 文件和字節碼,因而也就寫不下去了。

也許學習下來最大的收穫便是 弄清楚了 AspectJ 和 Spring AOP 在實現上幾乎無關。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章