JAVA 靜態代理 & 動態代理

Erosion2020發表於2024-11-21

Java中,代理模式是一種常見的設計模式,用於為某物件提供一種代理,以控制對該物件的訪問。根據代理類的實現方式,可以分為靜態代理和動態代理。以下將分別介紹這兩種方式,並進行對比分析。

靜態代理

靜態代理是指在編譯時期就已經確定了代理類的實現。代理類需要實現與目標物件相同的介面,並持有目標物件的引用,透過代理物件呼叫目標物件的方法。

靜態代理的實現步驟

  1. 定義介面。
  2. 編寫介面的實現類(目標類)。
  3. 編寫代理類,實現介面並持有目標類的引用。
  4. 在代理類中,透過目標類的引用呼叫實際的方法。

示例程式碼

// 定義介面
public interface Hello {
    void morning(String name);
}

// 目標類
public class HelloWorld implements Hello {
    @Override
    public void morning(String name) {
        System.out.println("Good morning, " + name);
    }
}

// 代理類
public class HelloProxy implements Hello {
    // 內部維護一個目標代理物件的屬性欄位
    private Hello target;
    public HelloProxy(Hello target) {
        this.target = target;
    }

    @Override
    public void morning(String name) {
        System.out.println("Before method invoke...");
        target.morning(name); // 呼叫目標類方法
        System.out.println("After method invoke...");
    }
}

// 測試靜態代理
public class Main {
    public static void main(String[] args) {
        Hello target = new HelloWorld();       // 建立目標物件
        Hello proxy = new HelloProxy(target); // 建立代理物件
        proxy.morning("Bob");                 // 呼叫代理方法
    }
}

輸出:

Before method invoke...
Good morning, Bob
After method invoke...

優點

  • 結構簡單,容易理解。
  • 代理類可以擴充套件目標類的功能(如新增日誌、許可權校驗等)。

缺點

  • 每增加一個介面,都需要單獨編寫代理類,擴充套件性差。
  • 代理類的維護工作量較大。

動態代理

動態代理是指在執行時動態生成代理類,而不需要提前定義實現類。它的實現主要依賴於Java的java.lang.reflect.ProxyInvocationHandler

動態代理的實現步驟

  1. 定義介面。
  2. 建立InvocationHandler介面的實現類,用來處理方法呼叫。
  3. 使用Proxy.newProxyInstance()生成動態代理物件。

示例程式碼

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 定義介面
public interface Hello {
    void morning(String name);
}
// 測試動態代理
public class Main {
    public static void main(String[] args) {
        // 建立InvocationHandler
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("Before method invoke...");
                if (method.getName().equals("morning")) {
                    System.out.println("Good morning, " + args[0]);
                }
                System.out.println("After method invoke...");
                return null;
            }
        };
        // 建立動態代理物件
        Hello hello = (Hello) Proxy.newProxyInstance(
            Hello.class.getClassLoader(),     // 類載入器
            new Class[] { Hello.class },      // 需要實現的介面
            handler                           // 方法呼叫處理器
        );
        // 呼叫代理方法
        hello.morning("Bob");
    }
}

輸出

Before method invoke...
Good morning, Bob
After method invoke...

動態代理的核心:

  1. InvocationHandler介面:定義了代理邏輯的核心。
  2. Proxy.newProxyInstance():負責在執行時動態生成代理類並返回代理例項。

動態代理本質上是JVM在執行時生成代理類的位元組碼,並載入到記憶體中。

靜態代理與動態代理的對比

特點 靜態代理 動態代理
代理類實現方式 編譯時定義代理類 執行時動態生成代理類
對介面的支援 每個介面需要單獨實現代理類 可通用,只需實現InvocationHandler
程式碼擴充套件性 低,新增介面時需增加代理類 高,只需更改代理邏輯即可
效能開銷 無需反射,效能較高 依賴反射呼叫,效能略低
應用場景 小型專案或介面穩定的場景 大型專案、動態擴充套件功能的場景

總結

  • 靜態代理適用於小型專案,或者代理類相對固定的場景。
  • 動態代理適用於需要動態擴充套件功能或減少重複程式碼的場景。
  • 動態代理的靈活性更強,尤其是在AOP(面向切面程式設計)框架中廣泛使用,如Spring框架中的動態代理。

動態代理的強大之處在於,它將代理邏輯與具體實現解耦,使得程式碼更加靈活、可維護,同時提供了更高的複用性。

相關文章