Java中,代理模式是一種常見的設計模式,用於為某物件提供一種代理,以控制對該物件的訪問。根據代理類的實現方式,可以分為靜態代理和動態代理。以下將分別介紹這兩種方式,並進行對比分析。
靜態代理
靜態代理是指在編譯時期就已經確定了代理類的實現。代理類需要實現與目標物件相同的介面,並持有目標物件的引用,透過代理物件呼叫目標物件的方法。
靜態代理的實現步驟
- 定義介面。
- 編寫介面的實現類(目標類)。
- 編寫代理類,實現介面並持有目標類的引用。
- 在代理類中,透過目標類的引用呼叫實際的方法。
示例程式碼
// 定義介面
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.Proxy
和InvocationHandler
。
動態代理的實現步驟
- 定義介面。
- 建立
InvocationHandler
介面的實現類,用來處理方法呼叫。 - 使用
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...
動態代理的核心:
- InvocationHandler介面:定義了代理邏輯的核心。
- Proxy.newProxyInstance():負責在執行時動態生成代理類並返回代理例項。
動態代理本質上是JVM在執行時生成代理類的位元組碼,並載入到記憶體中。
靜態代理與動態代理的對比
特點 | 靜態代理 | 動態代理 |
---|---|---|
代理類實現方式 | 編譯時定義代理類 | 執行時動態生成代理類 |
對介面的支援 | 每個介面需要單獨實現代理類 | 可通用,只需實現InvocationHandler |
程式碼擴充套件性 | 低,新增介面時需增加代理類 | 高,只需更改代理邏輯即可 |
效能開銷 | 無需反射,效能較高 | 依賴反射呼叫,效能略低 |
應用場景 | 小型專案或介面穩定的場景 | 大型專案、動態擴充套件功能的場景 |
總結
- 靜態代理適用於小型專案,或者代理類相對固定的場景。
- 動態代理適用於需要動態擴充套件功能或減少重複程式碼的場景。
- 動態代理的靈活性更強,尤其是在AOP(面向切面程式設計)框架中廣泛使用,如Spring框架中的動態代理。
動態代理的強大之處在於,它將代理邏輯與具體實現解耦,使得程式碼更加靈活、可維護,同時提供了更高的複用性。