Java 動態代理原理圖解 (附:2種實現方式詳細對比)
動態代理在 Java 中有著廣泛的應用,例如:Spring AOP 面向切面程式設計,Hibernate 資料查詢、以及 RPC Dubbo 遠端呼叫等,都有非常多的實際應用@mikechen
目錄
- Java 動態代理原理
- JDK 原生動態代理
- CGLib 動態代理實現
- JDK 動態代理與 CGLib 的區別
Java 動態代理原理
按照代理的建立時期,代理類可以分為兩種:
- 靜態代理:由程式設計師建立或特定工具自動生成原始碼,再對其編譯,在程式執行前,代理類的 .class 檔案就已經存在了。
- 動態代理:在程式執行時,可以運用反射機制動態建立代理類的 .class 檔案。
動態代理類與靜態代理類最主要的不同點是:代理類的位元組碼不是在程式執行前生成的,而是在程式執行時再虛擬機器中程式自動建立的。
動態代理的實現方式很多。 例如:JDK 自身提供的動態代理,就利用了上面提到的反射機制。除了反射,動態代理還可以透過 CGLib 來實現,而 CGLib 是基於 ASM(一個 Java 位元組碼操作框架)而非反射實現的。
簡單來說,動態代理是一種行為方式,而 反射或 ASM 只是它的一種實現手段而已。
本文我主要詳解 Java 動態代理的 2 種主流現方式: JDK 原生動態代理與 CGLib 。
JDK 原生動態代理
JDK Proxy 動態代理的實現無需引用第三方類,只需要實現 InvocationHandler 介面,重寫 invoke() 方法即可,整個實現程式碼如下所示:、
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * JDK Proxy 相關示例 */ public class ProxyExample { static interface Car { void running(); } static class Bus implements Car { @Override public void running() { System.out.println("The bus is running."); } } static class Taxi implements Car { @Override public void running() { System.out.println("The taxi is running."); } } /** * JDK Proxy */ static class JDKProxy implements InvocationHandler { private Object target; // 代理物件 // 獲取到代理物件 public Object getInstance(Object target) { this.target = target; // 取得代理物件 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } /** * 執行代理方法 * @param proxy 代理物件 * @param method 代理方法 * @param args 方法的引數 * @return * @throws InvocationTargetException * @throws IllegalAccessException */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { System.out.println("動態代理之前的業務處理."); Object result = method.invoke(target, args); // 執行呼叫方法(此方法執行前後,可以進行相關業務處理) return result; } } public static void main(String[] args) { // 執行 JDK Proxy JDKProxy jdkProxy = new JDKProxy(); Car carInstance = (Car) jdkProxy.getInstance(new Taxi()); carInstance.running();
以上程式的執行結果是:
動態代理之前的業務處理。
The taxi is running.
可以看出, JDK Proxy 實現動態代理的核心是實現 Invocation 介面,我們檢視 Invocation 的原始碼,會發現裡面其實只有一個 invoke() 方法,原始碼如下:
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
這是因為在動態代理中有一個重要的角色,也就是代理器,它用於統一管理被代理的物件,顯然 InvocationHandler 就是這個代理器。而 invoke() 方法,則是觸發代理的執行方法,我們透過實現 Invocation 介面來擁有動態代理的能力。
CGLib 動態代理實現
CGLIB (Code Generation Library) 是一個基於 ASM 的位元組碼生成庫,它允許我們在執行時對位元組碼進行修改、和動態生成 CGLIB 透過繼承方式實現代理。
在使用 CGLib 之前,我們要先在專案中引入 CGLib 框架, 在 pom.xml 中新增如下配置:
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
CGLib 的實現程式碼:
package com.mikechen.proxydemo; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CGLibExample { static class Car { public void running() { System.out.println("The car is running."); } } /** * CGLib 代理類 */ static class CGLibProxy implements MethodInterceptor { private Object target; // 代理物件 public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); // 設定父類為例項類 enhancer.setSuperclass(this.target.getClass()); // 回撥方法 enhancer.setCallback(this); // 建立代理物件 return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("方法呼叫前業務處理."); Object result = methodProxy.invokeSuper(o, objects); // 執行方法呼叫 return result; } } // 執行 CGLib 的方法呼叫 public static void main(String[] args) { // 建立 CGLib 代理類 CGLibProxy proxy = new CGLibProxy(); // 初始化代理物件 Car car = (Car) proxy.getInstance(new Car()); // 執行方法 car.running();
以上程式的執行結果是:
方法呼叫前業務處理。
The car is running.
可以看出:
CGLib 和 JDK Proxy 的實現程式碼比較類似,都是透過實現代理器的介面,再呼叫某一個方法完成動態代理的。
唯一不同的是,CGLib 在初始化被代理類時,是透過 Enhancer 物件把代理物件設定為被代理類的子類,來實現動態代理的。
因此,被代理類不能被關鍵字 final 修飾,如果被 final 修飾,再使用 Enhancer 設定父類時會報錯,動態代理的構建會失敗。
JDK 動態代理與 CGLib 的區別
1. JDK 動態代理具體實現原理
- 透過實現 InvocationHandler 介面,建立自己的呼叫處理器;
- 透過為 Proxy 類指定 ClassLoader 物件和一組 interface ,來建立動態代理;
- 透過反射機制獲取動態代理類的建構函式,其唯一引數型別就是呼叫處理器介面型別;
- 透過建構函式建立動態代理類例項,構造時呼叫處理器物件作為引數參入。
2. CGLib 動態代理
CGLib 是一個強大、高效能的 Code 生產類庫,可以實現執行期動態擴充套件 java 類,Spring 在執行期間透過 CGlib 繼承要被動態代理的類,重寫父類的方法,實現 AOP 面向切面程式設計。
3. 兩者對比
- JDK 動態代理是面向介面的。
- CGLib 動態代理是透過位元組碼底層繼承要代理類來實現(如果被代理類被 final 關鍵字所修飾,會失敗)。
4. 效能對比
- CGLib 所建立的動態代理物件,在實際執行時候的效能要比 JDK 動態代理高不少,有研究表明,大概要高出10倍;
- CGLib 在建立物件的時候所花費的時間,比 JDK 動態代理要多很多,有研究表明,大概要高出8倍。
因此,對於 singleton 的代理物件或者具有例項池的代理,因為無需頻繁的建立代理物件,更適合採用 CGLib 動態代理,反之,則比較適用 JDK 動態代理。
以上,是關於 Java 動態代理原理、以及動態代理2 種實現方式的解析。
希望有所幫助,謝謝【 關注+ 點贊+ 轉發】 支援。
作者簡介
陳睿 | mikechen , 10年+大廠架構經驗,「mikechen 的網際網路架構」系列文章作者,專注於網際網路架構技術。
閱讀「mikechen 的網際網路架構」40W 字技術文章合集
Java併發 | JVM | MySQL | Spring | Redis | 分散式 | 高併發
以上合集,關注「mikechen 的網際網路架構」公眾號,回覆 【 架構】即可獲得。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70011997/viewspace-2921054/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- JDK動態代理實現原理詳解(原始碼分析)JDK原始碼
- 細說JDK動態代理的實現原理JDK
- Java動態代理 jdk和cglib的實現比較JavaJDKCGLib
- java執行緒實現的三種方式以及靜態代理Java執行緒
- java幾種代理模式的實現方式Java模式
- CGLib動態代理原理及實現CGLib
- RPC核心實現原理-動態代理RPC
- Linux安裝JDK兩種方式詳細教程(附圖)LinuxJDK
- Java-JDK動態代理(AOP)使用及實現原理分析JavaJDK
- 幀動畫的多種實現方式與效能對比動畫
- Java代理設計模式(Proxy)的四種具體實現:靜態代理和動態代理Java設計模式
- 大廠高階工程師面試必問系列:Java動態代理機制和實現原理詳解工程師面試Java
- 代理模式詳解:靜態代理、JDK動態代理與Cglib動態代理模式JDKCGLib
- JDK動態代理詳解JDK
- Java進階 | Proxy動態代理機制詳解Java
- 靜態代理和動態代理(jdk/cglib)詳解JDKCGLib
- java執行原理、靜態代理和動態代理區分Java
- (十七)關於動態代理,你能說出動態代理的幾種方式?
- js實現繼承的幾種方式和對比JS繼承
- 動態代理的兩種方式以及區別
- SAP ABAP和Java的動態代理實現Java
- Java 靜態代理和動態代理的使用及原理解析Java
- 淺析Spring中AOP的實現原理——動態代理Spring
- 堆排序的Python實現(附詳細過程圖和講解)排序Python
- 【乾貨】JDK動態代理的實現原理以及如何手寫一個JDK動態代理JDK
- Spring原始碼剖析5:JDK和cglib動態代理原理詳解Spring原始碼JDKCGLib
- SmartSql使用教程(2)—使用動態代理實現CURDSQL
- Java中動態規則的實現方式Java
- 現代瀏覽器內部工作原理(附詳細流程圖)瀏覽器流程圖
- MyBatis進階--介面代理方式實現Dao 和動態SQLMyBatisSQL
- GPIO口工作原理的超詳細解釋(附電路圖)
- 多執行緒的三種實現方式及靜態代理模式執行緒模式
- 詳解 Java 中的三種代理模式Java模式
- 好程式設計師Java教程Java動態代理機制詳解程式設計師Java
- JAVA 靜態代理 & 動態代理Java
- Java中的靜態代理模式詳解Java模式
- 多種跨域方式實現原理跨域
- 九種跨域方式實現原理跨域