代理模式為其他對象提供一個代理對象以控制對目標對象的存取。適用於:
| 角色 | 職責 | 關鍵點 |
|---|---|---|
| 抽象角色 | 聲明目標與代理共同介面 | Java 中用 interface 或 abstract class |
| 真實角色 | 實際業務邏輯實現 | 最終被調用的對象 |
| 代理角色 | 持有真實對象引用,實現同一介面,轉發調用並可增強 | 靜態代理:編譯期確定;動態代理:運行時生成 |
public interface Subject {
void request();
}
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("From real subject.");
}
}
public class ProxySubject implements Subject {
private RealSubject realSubject; // 持有真實對象引用
@Override
public void request() {
preRequest(); // 前置增強
if (realSubject == null) realSubject = new RealSubject();
realSubject.request(); // 委派真實對象
postRequest(); // 後置增強
}
private void preRequest() { /* 日誌、權限檢查、緩存查找 */ }
private void postRequest() { /* 耗時統計、結果緩存、資源清理 */ }
}
Subject sub = new ProxySubject();
sub.request(); // 實際執行 ProxySubject.request()
| 類/介面 | 包 | 核心作用 |
|---|---|---|
InvocationHandler | java.lang.reflect | 代理邏輯處理器,單一方法 invoke(proxy, method, args) |
Proxy | java.lang.reflect | 動態代理類生成器,靜態工廠方法 newProxyInstance |
public interface Subject {
void request();
}
public class RealSubject implements Subject {
@Override public void request() { System.out.println("From real subject."); }
}
import java.lang.reflect.*;
public class DynamicInvocationHandler implements InvocationHandler {
private final Object target; // 持有真實對象,類型為 Object 解耦具體類
public DynamicInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before calling: " + method.getName());
Object result = method.invoke(target, args); // 反射調用真實方法
System.out.println("After calling: " + method.getName());
return result;
}
}
import java.lang.reflect.*;
public class Client {
public static void main(String[] args) {
RealSubject real = new RealSubject();
InvocationHandler handler = new DynamicInvocationHandler(real);
// 一步生成代理實例
Subject proxy = (Subject) Proxy.newProxyInstance(
real.getClass().getClassLoader(), // 類加載器
real.getClass().getInterfaces(), // 代理實現的介面陣列
handler // 處理器
);
proxy.request(); // 觸發 handler.invoke()
// 驗證代理類特性
System.out.println("Proxy class: " + proxy.getClass().getName()); // $Proxy0
System.out.println("Is Proxy: " + Proxy.isProxyClass(proxy.getClass())); // true
}
}
ProxyGenerator.generateProxyClass)InvocationHandler)、實現介面方法(委派給 handler.invoke)Proxy,但 not 真實類| 維度 | 靜態代理 | JDK 動態代理 |
|---|---|---|
| 代理類產生時間 | 編譯期 | 運行時 |
| 代理類數量 | 每個目標類一個 | 單一通用邏輯服務多目標 |
| 目標類型要求 | 任意類 | 必須實現介面 |
| 方法攔截粒度 | 手寫控制 | 統一 invoke 攔截所有方法 |
| 效能開銷 | 直接調用 | 反射調用(JDK 8+ 已大幅優化) |
| 適用場景 | 固定、少量、簡單增強 | 框架級、介面導向、批量攔截 |
JDK 動態代理要求目標實現介面。CGLIB(Code Generation Library)通過繼承目標類並重寫非 final 方法,實現無介面代理。Spring AOP 默認:有介面用 JDK,無介面用 CGLIB。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
3.3.0
</dependency>
| 特性 | JDK Proxy | CGLIB |
|---|---|---|
| 核心類 | Proxy | Enhancer |
| 回調介面 | InvocationHandler | MethodInterceptor |
| 代理方式 | 實現介面 | 繼承子類 |
| final 方法 | 可代理 | 無法攔截(無法重寫) |
| 私有方法 | 不可代理 | 不可代理 |
| 生成類命名 | $Proxy0 | $$EnhancerByCGLIB$$TargetClass$$uuid |
import net.sf.cglib.proxy.*;
public class CglibProxyDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class); // 目標類無需介面
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
System.out.println("CGLIB Before: " + method.getName());
Object result = proxy.invokeSuper(obj, args); // 調用父類方法
System.out.println("CGLIB After: " + method.getName());
return result;
}
});
RealSubject proxy = (RealSubject) enhancer.create();
proxy.request();
}
}
Spring AOP 通過 ProxyChain 實現多個 Advice 串聯,每個 invoke 決定是否調用 chain.doProxy() 傳遞給下一層。
JDK 動態代理實例無法直接序列化(不實現 Serializable)。解決:自定義 ProxyClassFactory 生成實現 Serializable 的代理類,或使用 CGLIB 並實現 Serializable。
MethodHandle / VarHandle 替代反射invoke 內做重型邏輯,異步化橫切關注點ProxyClass(JDK 內部已緩存)-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
// 生成的 $Proxy0.class 會寫入當前目錄,可用 javap 反編譯分析
代理模式是 AOP、RPC、緩存、事務等框架基礎設施的基石。JDK 動態代理以介面為契約、反射為機制,構建了 Java 標準庫級的運行時類型生成能力;CGLIB通過字節碼繼承補足了無介面場景。理解兩者生成原理、適用邊界、效能特徵,能讓你在框架選型、自定義攔截器、排查代理相關 Bug 時遊刃有餘。