Spring開發:動態代理的藝術與實踐

本文分享自華爲雲社區《Spring高手之路17——動態代理的藝術與實踐》,作者: 磚業洋__。

1. 背景

動態代理是一種強大的設計模式,它允許開發者在運行時創建代理對象,用於攔截對真實對象的方法調用。這種技術在實現面向切面編程(AOP)、事務管理、權限控制等功能時特別有用,因爲它可以在不修改原有代碼結構的前提下,爲程序動態地注入額外的邏輯。

2. JDK動態代理

2.1 定義和演示

JDK動態代理是Java語言提供的一種基於接口的代理機制,允許開發者在運行時動態地創建代理對象,而無需爲每個類編寫具體的代理實現。

這種機制主要通過 java.lang.reflect.Proxy 類和 java.lang.reflect.InvocationHandler 接口實現。下面是JDK動態代理的核心要點和如何使用它們的概述。

使用步驟

  1. 定義接口:首先定義一個或多個接口,代理對象將實現這些接口。

  2. 實現接口:創建一個類,它實現上述接口,提供具體的實現邏輯。

  3. 創建 InvocationHandler 實現:定義一個 InvocationHandler 的實現,這個實現中的 invoke 方法可以包含自定義邏輯。

  4. 創建代理對象:使用 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 方法是在代理實例上調用方法時被自動調用的。通過這種方式可以在實際的方法執行前後加入自定義的邏輯,比如日誌記錄、權限檢查等。

  • methodmethod 是一個 java.lang.reflect.Method 類的實例,代表了正在被調用的方法。在 invoke 方法中,這個對象用來標識代理對象上被調用的具體方法。

注意:如果嘗試直接在invoke方法內部使用method.invoke(proxy, args)調用代理對象的方法,而不是調用原始目標對象的方法,則會導致無限循環。這是因爲調用proxy實例上的方法會再次被代理攔截,從而無限調用invoke方法,傳參可別傳錯了。

  • invokeMethod 類的 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動態代理

CGLIBCode 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類來創建代理對象。EnhancerCGLIB中用於生成新類的類。

改造一下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變量決定是否執行
    }
}

運行結果如下:

我們需要注意幾點更改:

  1. 因爲CGLIB不是基於接口的代理,而是通過生成目標類的子類來實現代理,所以我們不再需要接口HelloWorld

  2. 我們將使用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;
        }
    }
}

運行結果

在這個改寫中,我們使用CGLIBEnhancerMethodInterceptor來代替了JDKProxyInvocationHandlerMethodInterceptorintercept方法與InvocationHandlerinvoke方法在概念上是相似的,但它使用MethodProxyinvokeSuper方法來調用原始類的方法,而不是使用反射。這允許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動態代理

CGLIBCode 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框架SpringAOP模塊和事務管理廣泛使用了動態代理技術。根據目標對象的類型(是否實現接口),Spring可以自動選擇JDK動態代理或CGLIB代理。

  • HibernateHibernate使用動態代理技術實現懶加載,代理實體類的關聯對象,在實際訪問這些對象時才從數據庫中加載它們的數據。

  • MyBatisMyBatis框架使用動態代理技術映射接口和SQL語句,允許開發者通過接口直接與數據庫交互,而無需實現類。

歡迎一鍵三連~

有問題請留言,大家一起探討學習

點擊關注,第一時間瞭解華爲雲新鮮技術~

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章