動態代理在 Java 中有著廣泛的應用,例如:Spring AOP 面向切面程式設計,Hibernate 資料查詢、以及 RPC Dubbo 遠端呼叫等,都有非常多的實際應用@mikechen
按照代理的建立時期,代理類可以分為兩種:
動態代理類與靜態代理類最主要的不同點是:代理類的位元組碼不是在程式執行前生成的,而是在程式執行時再虛擬機器器中程式自動建立的。
動態代理的實現方式很多。例如:JDK 自身提供的動態代理,就利用了上面提到的反射機制。除了反射,動態代理還可以通過 CGLib 來實現,而 CGLib 是基於 ASM(一個 Java 位元組碼操作框架)而非反射實現的。
簡單來說,動態代理是一種行為方式,而 反射或 ASM 只是它的一種實現手段而已。
本文我主要詳解 Java 動態代理的 2 種主流現方式:JDK 原生動態代理與 CGLib 。
JDK Proxy 動態代理的實現無需參照第三方類,只需要實現 InvocationHandler 介面,重寫 invoke() 方法即可,整個實現程式碼如下所示:、
以上程式的執行結果是:
動態代理之前的業務處理。
可以看出, JDK Proxy 實現動態代理的核心是實現 Invocation 介面,我們檢視 Invocation 的原始碼,會發現裡面其實只有一個 invoke() 方法,原始碼如下:
這是因為在動態代理中有一個重要的角色,也就是代理器,它用於統一管理被代理的物件,顯然 InvocationHandler 就是這個代理器。而 invoke() 方法,則是觸發代理的執行方法,我們通過實現 Invocation 介面來擁有動態代理的能力。
CGLIB (Code Generation Library) 是一個基於 ASM 的位元組碼生成庫,它允許我們在執行時對位元組碼進行修改、和動態生成 CGLIB 通過繼承方式實現代理。
在使用 CGLib 之前,我們要先在專案中引入 CGLib 框架,在 pom.xml 中新增如下設定:
CGLib 的實現程式碼:
以上程式的執行結果是:
方法呼叫前業務處理。
可以看出:
CGLib 和 JDK Proxy 的實現程式碼比較類似,都是通過實現代理器的介面,再呼叫某一個方法完成動態代理的。
唯一不同的是,CGLib 在初始化被代理類時,是通過 Enhancer 物件把代理物件設定為被代理類的子類,來實現動態代理的。
因此,被代理類不能被關鍵字 final 修飾,如果被 final 修飾,再使用 Enhancer 設定父類別時會報錯,動態代理的構建會失敗。
1. JDK 動態代理具體實現原理
2. CGLib 動態代理
CGLib 是一個強大、高效能的 Code 生產類庫,可以實現執行期動態擴充套件 java 類,Spring 在執行期間通過 CGlib 繼承要被動態代理的類,重寫父類別的方法,實現 AOP 面向切面程式設計。
3. 兩者對比
4. 效能對比
因此,對於 singleton 的代理物件或者具有範例池的代理,因為無需頻繁的建立代理物件,更適合採用 CGLib 動態代理,反之,則比較適用 JDK 動態代理。
以上,是關於 Java 動態代理原理、以及動態代理2 種實現方式的解析。
希望有所幫助,謝謝【關注+點贊+轉發】支援。
陳睿 | mikechen , 10年+大廠架構經驗,「mikechen 的網際網路架構」系列文章作者,專注於網際網路架構技術。