本文分享自華為雲社群《Spring高手之路17——動態代理的藝術與實踐》,作者: 磚業洋__。
1. 背景
動態代理是一種強大的設計模式,它允許開發者在執行時建立代理物件,用於攔截對真實物件的方法呼叫。這種技術在實現面向切面程式設計(AOP
)、事務管理、許可權控制等功能時特別有用,因為它可以在不修改原有程式碼結構的前提下,為程式動態地注入額外的邏輯。
2. JDK動態代理
2.1 定義和演示
JDK
動態代理是Java
語言提供的一種基於介面的代理機制,允許開發者在執行時動態地建立代理物件,而無需為每個類編寫具體的代理實現。
這種機制主要透過 java.lang.reflect.Proxy
類和 java.lang.reflect.InvocationHandler
介面實現。下面是JDK
動態代理的核心要點和如何使用它們的概述。
使用步驟
-
定義介面:首先定義一個或多個介面,代理物件將實現這些介面。
-
實現介面:建立一個類,它實現上述介面,提供具體的實現邏輯。
-
建立
InvocationHandler
實現:定義一個InvocationHandler
的實現,這個實現中的invoke
方法可以包含自定義邏輯。 -
建立代理物件:使用
Proxy.newProxyInstance
方法,傳入目標物件的類載入器、需要代理的介面陣列以及InvocationHandler
的實現,來建立一個實現了指定介面的代理物件。
用簡單的例子來說明這個過程,全部程式碼如下:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface HelloWorld { void sayHello(); } class HelloWorldImpl implements HelloWorld { public void sayHello() { System.out.println("Hello world!"); } } public class DemoApplication { public static void main(String[] args) { HelloWorldImpl realObject = new HelloWorldImpl(); HelloWorld proxyInstance = (HelloWorld) Proxy.newProxyInstance( HelloWorldImpl.class.getClassLoader(), // 使用目標類的類載入器 new Class[]{HelloWorld.class}, // 代理類需要實現的介面列表 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 在呼叫目標方法前可以插入自定義邏輯 System.out.println("Before method call"); // 呼叫目標物件的方法 Object result = method.invoke(realObject, args); // 在呼叫目標方法後可以插入自定義邏輯 System.out.println("After method call"); return result; } }); proxyInstance.sayHello(); } }
執行結果如下:
InvocationHandler
是動態代理的核心介面之一,當我們使用動態代理模式建立代理物件時,任何對代理物件的方法呼叫都會被轉發到一個實現了 InvocationHandler
介面的例項的 invoke
方法上。
我們經常看到InvocationHandler
動態代理的匿名內部類,這會在代理物件的相應方法被呼叫時執行。具體地說,每當對代理物件執行方法呼叫時,呼叫的方法不會直接執行,而是轉發到實現了InvocationHandler
的 invoke
方法上。在這個 invoke
方法內部,我們可以定義攔截邏輯、呼叫原始物件的方法、修改返回值等操作。
在這個例子中,當呼叫 proxyInstance.sayHello()
方法時,實際上執行的是 InvocationHandler
的匿名內部類中的 invoke
方法。這個方法中,我們可以在呼叫實際物件的 sayHello
方法前後新增自定義邏輯(比如這裡的列印訊息)。這就是動態代理和 InvocationHandler
的工作原理。
我們來看關鍵的一句程式碼
Object result = method.invoke(realObject, args);
在Java
的動態代理中,method.invoke(realObject, args)
這句程式碼扮演著核心的角色,因為它實現了代理物件方法呼叫的轉發機制。下面分別解釋一下這行程式碼的兩個主要部分:method.invoke()
方法和 args
引數。
method.invoke(realObject, args)
-
作用:這行程式碼的作用是呼叫目標物件(
realObject
)的具體方法。在動態代理的上下文中,invoke
方法是在代理例項上呼叫方法時被自動呼叫的。透過這種方式可以在實際的方法執行前後加入自定義的邏輯,比如日誌記錄、許可權檢查等。 -
method:
method
是一個java.lang.reflect.Method
類的例項,代表了正在被呼叫的方法。在invoke
方法中,這個物件用來標識代理物件上被呼叫的具體方法。
注意:如果嘗試直接在invoke
方法內部使用method.invoke(proxy, args)
呼叫代理物件的方法,而不是呼叫原始目標物件的方法,則會導致無限迴圈。這是因為呼叫proxy
例項上的方法會再次被代理攔截,從而無限呼叫invoke
方法,傳參可別傳錯了。
- invoke:
Method
類的invoke
方法用於執行指定方法。第一個引數是指明方法應該在哪個物件上呼叫(在這個例子中是realObject
),後續的引數args
是呼叫方法時傳遞的引數。
args
-
定義:
args
是一個物件陣列,包含了呼叫代理方法時傳遞給方法的引數值。如果被呼叫的方法沒有引數,args
將會是null
或者空陣列。 -
作用:
args
允許在invoke
方法內部傳遞引數給實際要執行的方法。這意味著可以在動態代理中不僅控制是否呼叫某個方法,還可以修改呼叫該方法時使用的引數。
2.2 不同方法分別代理
我們繼續透過擴充套件 HelloWorld
介面來包含多個方法,並透過JDK
動態代理演示許可權控制和功能開關操作的一種實現方式
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface HelloWorld { void sayHello(); void sayGoodbye(); } class HelloWorldImpl implements HelloWorld { public void sayHello() { System.out.println("Hello world!"); } public void sayGoodbye() { System.out.println("Goodbye world!"); } } public class DemoApplication { public static void main(String[] args) { HelloWorld realObject = new HelloWorldImpl(); HelloWorld proxyInstance = (HelloWorld) Proxy.newProxyInstance( HelloWorldImpl.class.getClassLoader(), new Class[]{HelloWorld.class}, new InvocationHandler() { // 新增一個簡單的許可權控制演示 private boolean accessAllowed = true; // 簡單的功能開關 private boolean goodbyeFunctionEnabled = true; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 許可權控制 if (!accessAllowed) { System.out.println("Access denied"); return null; // 在實際場景中,可以丟擲一個異常 } // 功能開關 if (method.getName().equals("sayGoodbye") && !goodbyeFunctionEnabled) { System.out.println("Goodbye function is disabled"); return null; } // 方法執行前的通用邏輯 System.out.println("Before method: " + method.getName()); // 執行方法 Object result = method.invoke(realObject, args); // 方法執行後的通用邏輯 System.out.println("After method: " + method.getName()); return result; } }); // 正常執行 proxyInstance.sayHello(); // 可以根據goodbyeFunctionEnabled變數決定是否執行 proxyInstance.sayGoodbye(); } }
執行如下:
如果accessAllowed
變數為false
:
如果goodbyeFunctionEnabled
變數為false
:
在這個例子中:
-
許可權控制:透過檢查
accessAllowed
變數,我們可以模擬簡單的許可權控制。如果沒有許可權,可以直接返回或丟擲異常,避免執行方法。 -
功能開關:透過檢查方法名稱和
goodbyeFunctionEnabled
變數,我們可以控制sayGoodbye
方法是否被執行。這可以用來根據配置啟用或禁用特定功能。
這個例子展示了JDK
動態代理在實際應用中如何進行方法級別的細粒度控制,同時保持程式碼的靈活性和可維護性。透過動態代理,我們可以在不修改原始類程式碼的情況下,為物件動態地新增額外的行為。
2.3 熔斷限流和日誌監控
為了更全面地展示JDK
動態代理的能力,我們在先前的示例中新增熔斷限流和日誌監控的邏輯。這些是在高併發和分散式系統中常見的需求,可以透過動態代理以非侵入式的方式實現。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; interface HelloWorld { void sayHello(); } class HelloWorldImpl implements HelloWorld { public void sayHello() { System.out.println("Hello world!"); } } public class DemoApplication { public static void main(String[] args) { HelloWorld realObject = new HelloWorldImpl(); HelloWorld proxyInstance = (HelloWorld) Proxy.newProxyInstance( HelloWorldImpl.class.getClassLoader(), new Class[]{HelloWorld.class}, new AdvancedInvocationHandler(realObject)); // 模擬多次呼叫以觀察限流和熔斷效果 for (int i = 0; i < 10; i++) { proxyInstance.sayHello(); } } static class AdvancedInvocationHandler implements InvocationHandler { private final Object target; private AtomicInteger requestCount = new AtomicInteger(0); private AtomicLong lastTimestamp = new AtomicLong(System.currentTimeMillis()); private volatile boolean circuitBreakerOpen = false; private final long cooldownPeriod = 10000; // 冷卻時間10秒 public AdvancedInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long now = System.currentTimeMillis(); // 檢查熔斷器是否應該被重置 if (circuitBreakerOpen && (now - lastTimestamp.get() > cooldownPeriod)) { circuitBreakerOpen = false; // 重置熔斷器 requestCount.set(0); // 重置請求計數 System.out.println("Circuit breaker has been reset."); } // 熔斷檢查 if (circuitBreakerOpen) { System.out.println("Circuit breaker is open. Blocking method execution for: " + method.getName()); return null; // 在實際場景中,可以返回一個兜底的響應或丟擲異常 } // 限流檢查 if (requestCount.incrementAndGet() > 5) { if (now - lastTimestamp.get() < cooldownPeriod) { // 10秒內超過5次請求,觸發熔斷 circuitBreakerOpen = true; lastTimestamp.set(now); // 更新時間戳 System.out.println("Too many requests. Opening circuit breaker."); return null; // 觸發熔斷時的處理 } else { // 重置計數器和時間戳 requestCount.set(0); lastTimestamp.set(now); } } // 執行實際方法 Object result = method.invoke(target, args); // 方法執行後的邏輯 System.out.println("Executed method: " + method.getName()); return result; } } }
在這個擴充套件示例中,我們實現了:
-
熔斷機制:透過一個簡單的計數器和時間戳來模擬。如果在
10
秒內對任一方法的呼叫次數超過5
次,我們就"開啟"熔斷器,阻止進一步的方法呼叫。在實際應用中,熔斷邏輯可能更加複雜,可能包括錯誤率的檢查、呼叫延遲的監控等。 -
限流:這裡使用的限流策略很簡單,透過計數和時間戳來判斷是否在短時間內請求過多。在更復雜的場景中,可以使用令牌桶或漏桶演算法等更高階的限流策略。
-
日誌監控:在方法呼叫前後列印日誌,這對於監控系統的行為和效能是非常有用的。在實際專案中,這些日誌可以整合到日誌管理系統中,用於問題診斷和效能分析。
透過在 invoke
方法中加入這些邏輯,我們能夠在不修改原有業務程式碼的情況下,為系統新增複雜的控制和監控功能。如果到達流量閾值或系統處於熔斷狀態,可以阻止對後端服務的進一步呼叫,直接返回一個預設值或錯誤響應,避免系統過載。
3. CGLIB動態代理
CGLIB
(Code Generation Library
)是一個強大的高效能程式碼生成庫,它在執行時動態生成新的類。與JDK
動態代理不同,CGLIB
能夠代理那些沒有實現介面的類。這使得CGLIB
成為那些因為設計限制或其他原因不能使用介面的場景的理想選擇。
3.1 定義和演示
工作原理
CGLIB
透過繼承目標類並在執行時生成子類來實現動態代理。代理類覆蓋了目標類的非final
方法,並在呼叫方法前後提供了注入自定義邏輯的能力。這種方法的一個關鍵優勢是它不需要目標物件實現任何介面。
使用CGLIB的步驟
新增CGLIB依賴:首先,需要在專案中新增CGLIB
庫的依賴。
Maven
,可以新增如下依賴到pom.xml
中:<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> <!-- 目前最新的版本 --> </dependency>
建立MethodInterceptor:實現MethodInterceptor
介面,這是CGLIB
提供的回撥型別,用於定義方法呼叫的攔截邏輯。
生成代理物件:使用Enhancer
類來建立代理物件。Enhancer
是CGLIB
中用於生成新類的類。
改造一下1.1
節的例子,可以對比看看,全部示例程式碼如下:
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; class HelloWorld { public void sayHello() { System.out.println("Hello world!"); } } public class DemoApplication { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); // 設定需要代理的類 enhancer.setSuperclass(HelloWorld.class); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Before method call"); Object result = proxy.invokeSuper(obj, args); // 呼叫父類的方法 System.out.println("After method call"); return result; } }); HelloWorld proxy = (HelloWorld) enhancer.create(); // 建立代理物件 proxy.sayHello(); // 透過代理物件呼叫方法 } }
執行結果如下:
CGLIB vs JDK動態代理
- 介面要求:
JDK
動態代理只能代理實現了介面的物件,而CGLIB
能夠直接代理類。 - 效能:
CGLIB
在生成代理物件時通常比JDK
動態代理要慢,因為它需要動態生成新的類。但在呼叫代理方法時,CGLIB
通常會提供更好的效能。 - 方法限制:
CGLIB
不能代理final
方法,因為它們不能被子類覆蓋。
CGLIB
是一個強大的工具,特別適用於需要代理沒有實現介面的類的場景。然而,選擇JDK
動態代理還是CGLIB
主要取決於具體的應用場景和效能要求。
注意:在CGLIB
中,如果使用MethodProxy.invoke(obj, args)
,而不是MethodProxy.invokeSuper(obj, args)
,並且obj
是代理例項本身(CGLIB
透過Enhancer
建立的代理物件,而不是原始的被代理的目標物件),就會導致無限迴圈。invoke
方法實際上是嘗試在傳遞的物件上呼叫方法,如果該物件是代理物件,則呼叫會再次被攔截,造成無限迴圈。
-
在
JDK
動態代理中,確保呼叫method.invoke
時使用的是目標物件,而不是代理物件。 -
在
CGLIB
代理中,使用MethodProxy.invokeSuper
而不是MethodProxy.invoke
來呼叫被代理的方法,以避免無限迴圈。
3.2 不同方法分別代理(對比JDK動態代理寫法)
我們改寫1.2
節的例子
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; class HelloWorldImpl { public void sayHello() { System.out.println("Hello world!"); } public void sayGoodbye() { System.out.println("Goodbye world!"); } } public class DemoApplication { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(HelloWorldImpl.class); // 設定被代理的類 enhancer.setCallback(new MethodInterceptor() { // 新增一個簡單的許可權控制演示 private boolean accessAllowed = true; // 簡單的功能開關 private boolean goodbyeFunctionEnabled = true; @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 許可權控制 if (!accessAllowed) { System.out.println("Access denied"); return null; // 在實際場景中,可以丟擲一個異常 } // 功能開關 if (method.getName().equals("sayGoodbye") && !goodbyeFunctionEnabled) { System.out.println("Goodbye function is disabled"); return null; } // 方法執行前的通用邏輯 System.out.println("Before method: " + method.getName()); // 執行方法 Object result = proxy.invokeSuper(obj, args); // 方法執行後的通用邏輯 System.out.println("After method: " + method.getName()); return result; } }); HelloWorldImpl proxyInstance = (HelloWorldImpl) enhancer.create(); // 建立代理物件 proxyInstance.sayHello(); // 正常執行 proxyInstance.sayGoodbye(); // 可以根據goodbyeFunctionEnabled變數決定是否執行 } }
執行結果如下:
我們需要注意幾點更改:
-
因為
CGLIB
不是基於介面的代理,而是透過生成目標類的子類來實現代理,所以我們不再需要介面HelloWorld
。 -
我們將使用
Enhancer
類來建立代理例項,並提供一個MethodInterceptor
來處理方法呼叫。
3.3 熔斷限流和日誌監控(對比JDK動態代理寫法)
我們改寫1.3
節的例子
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; class HelloWorld { void sayHello() { System.out.println("Hello world!"); } } public class DemoApplication { public static void main(String[] args) { HelloWorld realObject = new HelloWorld(); HelloWorld proxyInstance = (HelloWorld) createProxy(realObject); // 模擬多次呼叫以觀察限流和熔斷效果 for (int i = 0; i < 10; i++) { proxyInstance.sayHello(); } } public static Object createProxy(final Object realObject) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(HelloWorld.class); enhancer.setCallback(new AdvancedMethodInterceptor(realObject)); return enhancer.create(); } static class AdvancedMethodInterceptor implements MethodInterceptor { private final Object target; private final AtomicInteger requestCount = new AtomicInteger(0); private final AtomicLong lastTimestamp = new AtomicLong(System.currentTimeMillis()); private volatile boolean circuitBreakerOpen = false; private final long cooldownPeriod = 10000; // 冷卻時間10秒 public AdvancedMethodInterceptor(Object target) { this.target = target; } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { long now = System.currentTimeMillis(); // 檢查熔斷器是否應該被重置 if (circuitBreakerOpen && (now - lastTimestamp.get() > cooldownPeriod)) { circuitBreakerOpen = false; // 重置熔斷器 requestCount.set(0); // 重置請求計數 System.out.println("Circuit breaker has been reset."); } // 熔斷檢查 if (circuitBreakerOpen) { System.out.println("Circuit breaker is open. Blocking method execution for: " + method.getName()); return null; // 在實際場景中,可以返回一個兜底的響應或丟擲異常 } // 限流檢查 if (requestCount.incrementAndGet() > 5) { if (now - lastTimestamp.get() < cooldownPeriod) { // 10秒內超過5次請求,觸發熔斷 circuitBreakerOpen = true; lastTimestamp.set(now); // 更新時間戳 System.out.println("Too many requests. Opening circuit breaker."); return null; // 觸發熔斷時的處理 } else { // 重置計數器和時間戳 requestCount.set(0); lastTimestamp.set(now); } } // 執行實際方法 Object result = proxy.invokeSuper(obj, args); // 注意這裡呼叫的是invokeSuper // 方法執行後的邏輯 System.out.println("Executed method: " + method.getName()); return result; } } }
執行結果
在這個改寫中,我們使用CGLIB
的Enhancer
和MethodInterceptor
來代替了JDK
的Proxy
和InvocationHandler
。MethodInterceptor
的intercept
方法與InvocationHandler
的invoke
方法在概念上是相似的,但它使用MethodProxy
的invokeSuper
方法來呼叫原始類的方法,而不是使用反射。這允許CGLIB
在執行時生成代理類的位元組碼,而不是依賴於反射,從而提高了效能。此外,circuitBreakerOpen
被宣告為volatile
,是確保其在多執行緒環境中的可見性。
4. 動態代理圖示
方法呼叫攔截:客戶端透過代理物件呼叫方法,此時方法呼叫被代理物件攔截。
轉發給處理器或方法攔截器:代理物件將方法呼叫轉發給一個特定的處理器,這取決於所使用的代理型別。對於JDK
動態代理,這個處理器是InvocationHandler
;對於CGLIB
代理,是MethodInterceptor
。
在實際執行目標物件的方法之前,處理器有機會執行一些額外的操作,例如日誌記錄、安全檢查或事務管理等。
呼叫目標物件的方法:處理器在必要時直接呼叫目標物件的方法。在JDK
動態代理中,這通常透過反射實現;而在CGLIB
中,可以透過MethodProxy.invokeSuper
方法呼叫。
方法呼叫完成後,處理器再次有機會執行額外操作,比如修改返回值、記錄執行時間或進行事務的提交或回滾。
返回給客戶端:最終,方法的返回值被透過代理物件返回給客戶端。
5. JDK動態代理 VS CGLIB動態代理對比
JDK動態代理
JDK
動態代理是Java
自帶的代理機制,它直接使用反射API
來呼叫方法。
優點:
-
無需第三方依賴:作為
Java
標準API
的一部分,使用JDK
動態代理不需要新增額外的庫或依賴。 -
介面導向:強制使用介面進行代理,這符合面向介面程式設計的原則,有助於保持程式碼的清晰和靈活。
缺點:
-
僅限介面:只能代理實現了介面的類,這在某些情況下限制了它的使用。
-
效能開銷:由於使用反射
API
進行方法呼叫,可能會有一定的效能開銷,尤其是在大量呼叫時。
CGLIB動態代理
CGLIB
(Code Generation Library
)透過在執行時生成被代理物件的子類來實現代理。
優點:
-
不需要介面:可以代理沒有實現任何介面的類,這提供了更大的靈活性。
-
效能較好:通常認為
CGLIB
的效能比JDK
動態代理要好,特別是在代理方法的呼叫上,因為CGLIB
使用了位元組碼生成技術,減少了使用反射的需要。
缺點:
-
第三方庫:需要新增
CGLIB
庫作為專案依賴。 -
無法代理final方法:由於
CGLIB
是透過生成子類的方式來代理的,所以無法代理那些被宣告為final
的方法。
效能比較
-
呼叫速度:
CGLIB
在代理方法呼叫方面通常比JDK
動態代理更快。這是因為CGLIB
透過直接操作位元組碼來生成新的類,避免了反射帶來的效能開銷。 -
啟動效能:
CGLIB
在生成代理物件時可能會比JDK
動態代理慢,因為它需要在執行時生成新的位元組碼。如果代理物件在應用啟動時就被建立,這可能會略微影響啟動時間。
選擇建議
-
如果類已經實現了介面,或者希望強制使用介面程式設計,那麼
JDK
動態代理是一個好選擇。 -
如果需要代理沒有實現介面的類,或者對效能有較高的要求,特別是在代理方法的呼叫上,
CGLIB
可能是更好的選擇。 -
在現代的
Java
應用中,很多框架(如Spring
)都提供了對這兩種代理方式的透明支援,並且可以根據實際情況自動選擇使用哪一種。例如,Spring AOP
預設會使用JDK
動態代理,但如果遇到沒有實現介面的類,它會退回到CGLIB
。
6. 動態代理的實際應用場景
面向切面程式設計(AOP
):-
問題解決:在不改變原有業務邏輯程式碼的情況下,為程式動態地新增額外的行為(如日誌記錄、效能監測、事務管理等)。
-
應用例項:
Spring AOP
使用動態代理為方法呼叫提供了宣告式事務管理、安全性檢查和日誌記錄等服務。根據目標物件是否實現介面,Spring AOP
可以選擇使用JDK
動態代理或CGLIB
代理。
-
問題解決:自動化處理資料庫事務的邊界,如開始、提交或回滾事務。
-
應用例項:
Spring
框架中的宣告式事務管理使用代理技術攔截那些被@Transactional
註解標記的類或方法,確保方法執行在正確的事務管理下進行。
-
問題解決:在執行敏感操作之前自動檢查使用者許可權,確保只有擁有足夠許可權的使用者才能執行某些操作。
-
應用例項:企業應用中,使用代理技術攔截使用者的請求,進行許可權驗證後才允許訪問特定的服務或執行操作。
-
問題解決:物件的某些屬性可能載入成本較高,透過代理技術,可以在實際使用這些屬性時才進行載入。
-
應用例項:
Hibernate
和其他ORM
框架使用代理技術實現了延遲載入(懶載入),以提高應用程式的效能和資源利用率。
-
問題解決:對第三方庫或已有服務進行包裝,新增額外的邏輯,如快取結果、引數校驗等。
-
應用例項:在微服務架構中,可以使用代理技術對服務客戶端進行增強,實現如重試、熔斷、限流等邏輯。
在現代框架中的應用
-
Spring框架:
Spring
的AOP
模組和事務管理廣泛使用了動態代理技術。根據目標物件的型別(是否實現介面),Spring
可以自動選擇JDK
動態代理或CGLIB
代理。 -
Hibernate:
Hibernate
使用動態代理技術實現懶載入,代理實體類的關聯物件,在實際訪問這些物件時才從資料庫中載入它們的資料。 -
MyBatis:
MyBatis
框架使用動態代理技術對映介面和SQL
語句,允許開發者透過介面直接與資料庫互動,而無需實現類。
歡迎一鍵三連~
有問題請留言,大家一起探討學習
點選關注,第一時間瞭解華為雲新鮮技術~