原文同步發表至我的博客【夜月歸途】html
原文連接:http://www.guitu18.com/se/java/2018-06-29/18.htmljava
本博客關於Java動態代理相關內容直達連接:spring
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(); } }
以匿名內部類形式建立代理對象,這樣子代碼清爽許多;
尾巴: