細說Spring——AOP詳解(使用CGLIB實現AOP)

一、動態代理實現AOP的缺陷

在上一篇文章細說Spring——AOP詳解(動態代理實現AOP)中講解了如何使用動態代理實現AOP,雖然Java動態代理為我們提供了非常靈活的代理機制,但Java動態代理是基于接口的,如果目標對象沒有實現接口我們該如何代理呢?這時候我們就需要使用CGLIB來實現AOP了。

二、CGLIB實現代理的原理

我們先創建一個目標對象

package demo1;

/**
 * Created by Yifan Jia on 2018/6/9.
 */
public class SomeService {
    public String doFirst() {
        System.out.println("執行doFirst()方法");
        return "abcde";
    }

    public void doSecond() {
        System.out.println("doSecond()方法");
    }

}

針對這個目標類,假如我們要使用動態代理實現AOP,那么我們只能在寫一個增強的接口,然后讓目標類實現增強接口,然后我們就可以使用動態代理實現目標類的增強,可是假如我們不想讓目標類實現其他的接口,那么我們就只能使用CGLIB技術來實現目標類的增強了。
CGLIB實現目標類增強的原理是這樣的:CGLIB會動態創建一個目標類的子類,然后返回該子類的對象,也就是增強對象,至于增強的邏輯則是在子類中完成的。我們知道子類要么和父類有一樣的功能,要么就比父類功能強大,所以CGLIB是通過創建目標類的子類對象來實現增強的,所以:

目標子類 = 目標類 + 增強邏輯

至于CGLIB底層是如何動態的生成一個目標類的子類,它是使用動態字節碼技術,我們知道我們編寫的Java對象都是先編譯為.class文件,然后由類加載器加載到內存中變為一個Java對象的,動態字節碼技術就是通過轉換字節碼生成新的類來實現改變一個類的內部邏輯的。至于更基礎的部分,我也沒有深入的研究,有興趣的可以自己研究一下。

三、使用CGLIB實現AOP

這里需要注意,我們需要導入CGLIB的相關包才能使用CGLIB,這里需要導入兩個包:

  • cglib-nodep-3.2.0.jar:使用nodep包不需要關聯asm的jar包,jar包內部包含asm的類.
  • cglib-3.2.0.jar:使用此jar包需要關聯asm的jar包,否則運行時報錯.

版本可以自行選擇,我是有的Maven導入的jar包,依賴如下:

    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib-nodep</artifactId>
      <version>3.2.0</version>
    </dependency>

    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.2.0</version>
    </dependency>

通過CGLIB代理實現如下:

  • 首先實現一個MethodInterceptor,方法調用會被轉發到該類的intercept()方法。
  • 然后在需要使用SomeService(目標對象)的時候,通過CGLIB動態代理獲取代理對象。

我們仍然使用上面的SomeService作為目標對象,然后我們實現一個MethodInterceptor,在實現MethodInterceptor之前,我們先看一下這個接口是什么:

public interface MethodInterceptor extends Callback {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable;
}

這里解釋一下各個參數的意思:

  • Object為由CGLib動態生成的代理類實例
  • Method為上文中實體類所調用的被代理的方法引用
  • Object[]為參數值列表
  • MethodProxy為生成的代理類對方法的代理引用。

下面是實現的一個MethodInterceptor,同時寫了創建增強對象的邏輯即myCglibCreator()方法,該方法返回增強對象。

public class CglibFactory implements MethodInterceptor {

    public SomeService myCglibCreator() {
        Enhancer enhancer = new Enhancer();
        //將目標類設置為父類,cglib動態代理增強的原理就是子類增強父類,cglib不能增強目標類為final的類
        //因為final類不能有子類
        enhancer.setSuperclass(SomeService.class);
        //設置回調接口,這里的MethodInterceptor實現類回調接口,而我們又實現了MethodInterceptor,其實
        //這里的回調接口就是本類對象,調用的方法其實就是intercept()方法
        enhancer.setCallback(this);
        //create()方法用于創建cglib動態代理對象
        return (SomeService) enhancer.create();
    }

    //回調接口的方法
    //回調接口的方法執行的條件是:代理對象執行目標方法時會調用回調接口的方法
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
        Object result = methodProxy.invokeSuper(o, objects);

        //這里實現將返回值字符串變為大寫的邏輯
        if(result != null) {
            result = ((String) result).toUpperCase();
        }
        return result;
    }

}

其中,Object result = methodProxy.invokeSuper(o, objects);調用代理類實例上的methodProxy方法的父類方法(即實體類SomeService中對應的方法),然后返回目標方法的返回值result,然后實現增強的邏輯,將返回的字符串變為大寫的。下面是測試代碼:

package demo1;

/**
 * Created by Yifan Jia on 2018/6/9.
 */
public class Test {
    public static void main(String[] args) {

        SomeService target = new SomeService();

        SomeService proxy = new CglibFactory(target).myCglibCreator();

        String result = proxy.doFirst();
        System.out.println(result);
        proxy.doSecond();
    }
}

結果截圖:


這里寫圖片描述

上述代碼中,我們通過CGLIBEnhancer來指定要代理的目標對象、實際處理代理邏輯的對象,最終通過調用create()方法得到代理對象,對這個對象所有非final方法的調用都會轉發給MethodInterceptor.intercept()方法,在intercept()方法里我們可以加入任何邏輯,比如修改方法參數,加入日志功能、安全檢查功能等;通過調用MethodProxy.invokeSuper()方法,我們將調用轉發給原始對象,具體到本例,就是SomeService的具體方法。CGLIGMethodInterceptor的作用跟JDK動態代理代理中的InvocationHandler很類似,都是方法調用的中轉站。

四、總結

我們學習了通過CGLIB實現動態增強,但是CGLIB也有其缺陷,那就是必須目標類必須是可以繼承的,如果目標類不可繼承,那么我們就無法使用CGLIB來增強該類,現在我們已經學習完了Spring AOP中兩種AOP的實現機制,我們可以稱JDK動態代理實現的AOP為面向接口的動態增強,將CGLIB實現的AOP稱為面向子類的動態增強。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 本文主要講實現AOP的 代理模式原理,以及靜態代理,動態代理的區別和具體實現。 對SpringAOP的概念和使用,...
    _Zy閱讀 765評論 0 1
  • **** AOP 面向切面編程 底層原理 代理!??! 今天AOP課程1、 Spring 傳統 AOP2、 Spri...
    luweicheng24閱讀 1,386評論 0 1
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,869評論 18 139
  • 前言 只有光頭才能變強 上一篇已經講解了Spring IOC知識點一網打盡!,這篇主要是講解Spring的AOP模...
    Java3y閱讀 6,901評論 8 181
  • 【關于嘉賓】大家好,我是胡文杰,一個熱愛學習的小伙伴,3個標簽介紹自己~ 1·終生學習實踐者2·DISC心理學實踐...
    持續行動家胡文杰閱讀 440評論 0 3