Cglib和jdk動態代理的區別(轉載)
原文地址:https://www.cnblogs.com/sandaman2019/p/12636727.html
- 動態代理解決了方法之間的緊耦合,在方法調用方法中間可動態進行附加操作
- IOC解決了類與類之間的緊耦合!
Cglib 和 jdk 動態代理的區別?
- Jdk動態代理:利用攔截器(必須實現 InvocationHandler )加上反射機制生成一個代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理
- Cglib動態代理:利用
ASM
框架,對代理對象類生成的 class 文件加載進來,通過修改其字節碼生成子類來處理
什么時候用 cglib 什么時候用 JDK 動態代理?
- 目標對象生成了接口 默認用 JDK 動態代理
- 如果目標對象使用了接口,可以強制使用 cglib
- 如果目標對象沒有實現接口,必須采用 cglib 庫, Spring 會自動在 JDK 動態代理和 cglib 之間轉換
JDK 動態代理和 cglib 字節碼生成的區別?
- JDK 動態代理只能對實現了接口的類生成代理,而不能針對類
- Cglib 是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法,並覆蓋其中方法的增強,但是因為采用的是繼承,所以該類或方法最好不要設置為 final ,對於 final 類或方法,是無法繼承的
Cglib 比 JDK 快?
- cglib 底層是 ASM 字節碼生成框架,但是字節碼技術生成代理類,在 JDK1.6 之前比使用 java 反射的效率要高
- 在 JDK1.6 之后逐步對 JDK 動態代理進行了優化,在調用次數比較少時效率高於 cglib 代理效率
- 只有在大量調用的時候 cglib 的效率高,但是在 JDK8 的時候JDK的效率已高於 cglib
- Cglib 不能對聲明 final 的方法進行代理,因為 cglib 是動態生成代理對象,final 關鍵字修飾的類不可變只能被引用不能被修改
Spring 如何選擇是用 JDK 還是 cglib?
- 當 bean 實現接口時,會用 JDK 代理模式
- 當 bean 沒有實現接口,用 cglib實現
- 可以強制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class="true"/>)
Cglib 原理
動態生成一個要代理的子類,子類重寫要代理的類的所有不是 final 的方法。在子類中采用方法攔截技術攔截所有的父類方法的調用,順勢織入橫切邏輯,它比 Java 反射的 jdk 動態代理要快
Cglib 是一個強大的、高性能的代碼生成包,它被廣泛應用在許多 AOP 框架中,為他們提供方法的攔截。
最底層的是字節碼 Bytecode ,字節碼是 java 為了保證依次運行,可以跨平台使用的一種虛擬指令格式
在字節碼文件之上的是ASM,只是一種直接操作字節碼的框架,應用 ASM 需要對 Java 字節碼、 class 結構比較熟悉
位於 ASM 上面的是 Cglib、groovy、beanshell,后來那個種並不是 Java 體系中的內容是腳本語言,他們通過 ASM 框架生成字節碼變相執行 Java 代碼,在JVM中程序執行不一定非要寫 java 代碼,只要能生成 java 字節碼, JVM
並不關系字節碼的來源
位於 cglib、groovy、beanshell之上的就是 hibernate 和 Spring AOP,最上面的是applications,既具體應用,一般是一個web項目或者本地跑一個程序、
使用 cglib 代碼對類做代理?
使用 cglib 定義不同的攔截策略?
構造函數不攔截方法
用 MethodInterceptor 和 Enhancer 一個動態代理
案例
Cglib 的第三方庫提供的動態代理
/**
* 動態代理:
* 特點:字節碼隨用隨創建,隨用隨加載
* 作用:不修改源碼的基礎上對方法增強
* 分類:
* 基於接口的動態代理
* 基於子類的動態代理
* 基於子類的動態代理:
* 涉及的類:Enhancer
* 提供者:第三方cglib庫
* 如何創建代理對象:
* 使用Enhancer類中的create方法
* 創建代理對象的要求:
* 被代理類不能是最終類
* newProxyInstance方法的參數:在使用代理時需要轉換成指定的對象
* ClassLoader:類加載器
* 他是用於加載代理對象字節碼的。和被代理對象使用相同的類加載器。固定寫法
* Callback:用於提供增強的代碼
* 他是讓我們寫如何代理。我們一般寫一個該接口的實現類,通常情況加都是匿名內部類,但不是必須的。
* 此接口的實現類,是誰用誰寫。
* 我們一般寫的都是該接口的子接口實現類,MethodInterceptor
*/
com.dynamic.cglib.Producer cglibProducer= (com.dynamic.cglib.Producer) Enhancer.create(
com.dynamic.cglib.Producer.class,
new MethodInterceptor() {
/**
* 執行被代理對象的任何方法都會經過該方法
* @param obj
* @param method
* @param args
* 以上三個參數和基於接口的動態代理中invoke方法的參數是一樣的
* @param proxy:當前執行方法的代理對象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object returnValue=null;
Float money=(Float)args[0];
if("saleProduct".equals(method.getName())){
returnValue= method.invoke(producer,money*0.8f);
}
return returnValue;
}
}
);
cglibProducer.saleProduct(100.0f);
JDK 本身提供的動態代理實現
/**
* 動態代理:
* 特點:字節碼隨用隨創建,隨用隨加載
* 作用:不修改源碼的基礎上對方法增強
* 分類:
* 基於接口的動態代理
* 基於子類的動態代理
* 基於接口的動態代理:
* 涉及的類:proxy
* 提供者:Jdk官方
* 如何創建代理對象:
* 使用Proxy類中的newProxyInstance方法
* 創建代理對象的要求:
* 被代理類最少實現一個接口,如果沒有則不能使用
* newProxyInstance方法的參數:在使用代理時需要轉換成指定的對象
* ClassLoader:類加載器
* 他是用於加載代理對象字節碼的。和被代理對象使用相同的類加載器。固定寫法
* Class[]:字節碼數組
* 它是用於讓代理對象和被代理對象有相同方法。固定寫法
* InvocationHandler:用於提供增強的代碼
* 他是讓我們寫如何代理。我們一般寫一個該接口的實現類,通常情況加都是匿名內部類,但不是必須的。
* 此接口的實現類,是誰用誰寫。
*/
IProducer proxyProducer= (IProducer) Proxy.newProxyInstance(
producer.getClass().getClassLoader(),
producer.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 作用:執行被代理對象的任何接口方法都會經過該方法
* @param proxy 代理對象的引用
* @param method 當前執行的方法
* @param args 當前執行方法所需的參數
* @return 和被代理對象有相同返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 提供增強的代碼
// 1、獲取方法執行的參數
Object returnValue=null;
Float money=(Float)args[0];
if("saleProduct".equals(method.getName())){
returnValue= method.invoke(producer,money*0.8f);
}
return returnValue;
}
}
);
總結
Jdk 中的動態代理
JDK 中的動態代理是通過反射類 Proxy 以及 InvocationHandler 回調接口實現的,但是 JDK 中所有要進行動態代理的類必須要實現一個接口,也就是說只能對該類所實現接口中定義的方法進行代理,這在實際編程中有一定的局限性,而且使用反射的效率也不高
Cglib 實現
使用 cglib 是實現動態代理,不受代理類必須實現接口的限制,因為 cglib 底層是用 ASM 框架,使用字節碼技術生成代理類,你使用 Java 反射的效率要高, cglib 不能對聲明 final 的方法進行代理,因為 cglib 原理是動態生成被代理類的子類
Cglib | JDK | |
---|---|---|
是否提供子類代理 | 是 | 否 |
是否提供接口代理 | 是(可強制) | 是 |
區別 | 必須依賴於 CGLib 的類庫,但是它需要類來實現任何接口代理的是指定的類生成一個子類,覆蓋其中的方法 | 通過 InvocationHandler 使用 Proxy.newProxyInstance 產生代理對象;被代理的對象必須要實現接口 |