本文分享自華爲雲社區《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
語句,允許開發者通過接口直接與數據庫交互,而無需實現類。
歡迎一鍵三連~
有問題請留言,大家一起探討學習