Cglib動態代理淺析

原文同步發表至我的博客【夜月歸途】html

原文連接:http://www.guitu18.com/se/java/2018-06-29/18.htmljava

本博客關於Java動態代理相關內容直達連接:spring

  1. JDK動態代理淺析
  2. Cglib動態代理淺析
  3. JDK動態代理深刻理解分析並手寫簡易JDK動態代理(上)
  4. JDK動態代理深刻理解分析並手寫簡易JDK動態代理(下)

Java中的動態代理設計模式是很是經典且很是重要的設計模式之一,在感嘆設計者的天才設計至於,咱們想去探究一下這個設計模式是如何來實現的;設計模式

著名的spring框架的AOP的原理就是Java的動態代理機制;框架

在Spring中動態代理是實現有兩種:JDK動態代理和Cglib動態代理,本篇分析的是Cglib動態代理的實現;ide

JDK動態代理以前已經作過度析了[ JDK動態代理淺析 ],經過案例咱們知道了,Java動態代理的強大之處;可是不難看出來使用JDK動態代理也有着它的侷限性,JDK動態代理是在JVM內部動態的生成class字節碼對象,可是JDK動態代理只能針對接口進行操做,也就是隻能對接口的實現類去進行代理;post

如今分析一下另外一種經常使用的動態代理技術:Cglib動態代理;性能

CGLIB(Code Generation Library)是一個開源項目;
是一個強大的,高性能,高質量的Code生成類庫,它能夠在運行期擴展Java類與實現Java接口;
CGLIB包的底層是經過使用一個小而快的字節碼處理框架ASM,來轉換字節碼並生成新的類;
Cglib是針對類來實現代理的,原理是對指定的業務類生成一個子類,並覆蓋其中業務方法實現代理;
因此Cglib能夠爲無接口的類直接作代理,固然有接口的類也是能夠的並沒有影響。 測試

由於採用的是繼承,因此不能對final修飾的類進行代理;Cglib的使用限制無疑要比JDK動態代理要寬鬆不少;ui

廢話很少說,先上案例;

這裏說明一下,若是是Spring框架那麼無所謂,Spring框架的spring-core.jar包中已經集成了cglib與asm;若是你要單獨使用CGLIB,那麼須要導入cglib的jar包和asm相關jar包;

Programmer類:

public class Programmer {
    public void work(String name) {
        System.out.println(name + " 正在工做...");
    }
}

 

CglibProxyFactory代理類:

public class CglibProxyFactory implements MethodInterceptor {
    private Object target;
    public CglibProxyFactory(Object target) {
        this.target = target;
    }
    public Object getProxyInstance() {
        // 建立 Enhancer 對象
        Enhancer enhancer = new Enhancer();
        // 設置目標對象的Class
        enhancer.setSuperclass(target.getClass());
        // 設置回調操做,至關於InvocationHandler
        enhancer.setCallback(this);
        return enhancer.create();
    }
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
            throws Throwable {
        System.out.println("開始事務...");
        // 兩種方式執行方法,第二行註釋掉的,和當前行代碼效果相同,下面會分析;
        Object invoke = method.invoke(target, args);
        // Object invoke = methodProxy.invokeSuper(proxy, args);
        System.out.println("提交/回滾事務...");
        return invoke;
    }
}

 

Cglib建立代理的方式很是簡單,是經過Enhancer對象,須要給它set兩個參數:

設置目標對象的Class; 設置回調操做,至關於InvocationHandler,須要實現MethodInterceptor 接口; 以後執行create()方法便可返回目標對象的代理對象;

這裏爲了方便,直接讓CglibProxyFactory實現了MethodInterceptor 接口,須要實現重寫MethodInterceptor.intercept()方法;

在intercept()方法中,其實跟InvocationHandler.invoke中作的事是同樣的,咱們須要調用method.invoke()去執行方法;

在案例中,還有註釋掉的一行:Object invoke = methodProxy.invokeSuper(proxy, args);

這個和上面的 Object invoke = method.invoke(target, args); 是同樣的效果,都是代理執行方法,下面進行說明;

從代碼中看出,intercept()的參數比JDK的invoke()多了一個,前三個參數跟JDK的invoke()方法同樣很少說,第四個參數MethodProxy methodProxy是當前代理對象執行的方法的一個代理,也就是MethodProxy methodProxy是第二個參數Method method的代理;

前面咱們說過,Cglib動態代理的機制是對指定的業務類生成一個子類,並覆蓋其中業務方法實現代理;也就是參數一Object proxy其實是咱們的目標對象Object target的一個子類;

咱們直接使用 method.invoke(target, args) 執行方法,和使用methodProxy.invokeSuper(proxy, args) 執行是一個什麼關係呢;

第一個被代理類target直接調用method執行沒問題,第二個實際上是target的子類proxy去掉用父的方法,我第一次看這裏也是有點繞哈,可是其實就是這麼個關係,這兩個方法執行的效果是同樣的,可是一般咱們只使用第一種方式;

測試執行,貼測試代碼:

public class CglibProxyFactoryTest {
    public static void main(String[] args) {
        Programmer programmer = new Programmer();
        CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(programmer);
        Programmer instance = (Programmer) cglibProxyFactory.getProxyInstance();
        instance.work("夜月歸途");
    }
}

 

控制檯輸出:

開始事務... 
夜月歸途 正在工做... 
提交/回滾事務...

看到這裏和JDK的動態代理是同樣的,Cglib的的動態代理沒有接口的限制,有接口的類和無接口的類均可以被代理,只要不是final的;

在這裏,一樣代碼能夠精簡一下:

public class CglibProxyFactory {
    public static Object getProxyInstance(Object target) {
        // 建立 Enhancer 對象
        Enhancer enhancer = new Enhancer();
        // 設置目標對象的Class
        enhancer.setSuperclass(target.getClass());
        // 設置回調操做,至關於InvocationHandler
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
                    throws Throwable {
                System.out.println("開始事務...");
                // Object invoke1 = method.invoke(target, args);
                Object invoke2 = methodProxy.invokeSuper(proxy, args);
                System.out.println("提交/回滾事務...");
                return invoke2;
            }
        });
        return enhancer.create();
    }
}

 

以匿名內部類形式建立代理對象,這樣子代碼清爽許多;

尾巴:

  1. 靜態代理是經過在代碼中顯式定義一個業務實現類一個代理,在代理類中對同名的業務方法進行包裝,用戶經過代理類調用被包裝過的業務方法;
  2. JDK動態代理是經過接口中的方法名,在動態生成的代理類中調用業務實現類的同名方法;
  3. CGlib動態代理是經過繼承業務類,生成的動態代理類是業務類的子類,經過重寫業務方法進行代理;
  4. 若是目標對象有接口,優先使用jdk動態代理,若是目標對象無接口,使用cglib動態代理,如:Spring框架的代理選擇機制就是這樣的;
相關文章
相關標籤/搜索