| 參數 | 含義 | 說明 |
| proxy | 代理對象自身引用 | 通常不直接使用,避免遞歸調用 |
| method | 被調用的 Method 對象 | 包含方法名、參數類型、返回類型、註解等元數據 |
| args | 實際參數值數組 | 基本類型自動裝箱,null 表示無參數 |
Proxy — 動態代理類的工廠
// 核心靜態工廠方法
public static Object newProxyInstance(
ClassLoader loader, // 代理類加載器
Class>[] interfaces, // 代理實現的接口列表
InvocationHandler h // 方法調用處理器
) throws IllegalArgumentException
- loader:定義代理類由哪個 ClassLoader 加載,通常用
handler.getClass().getClassLoader() 或目標類加載器
- interfaces:代理類**實現**的接口集合,**多態**體現——代理對象可轉型為任意接口類型
- h:關聯的 InvocationHandler,所有接口方法調用均委派給其
invoke()
完整實戰案例:房屋租賃代理
1. 定義業務接口
public interface Subject {
void rent();
void hello(String str);
}
2. 真實業務實現
public class RealSubject implements Subject {
@Override
public void rent() {
System.out.println("I want to rent my house");
}
@Override
public void hello(String str) {
System.out.println("hello: " + str);
}
}
3. 動態代理處理器 — 核心邏輯
public class DynamicProxy implements InvocationHandler {
private Object subject; // 持有真實對象引用
public DynamicProxy(Object subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增強
System.out.println("before rent house");
System.out.println("Method: " + method);
// 委派真實對象執行
Object result = method.invoke(subject, args);
// 後置增強
System.out.println("after rent house");
return result;
}
}
4. 客戶端啟動與驗證
public class Client {
public static void main(String[] args) {
// 真實對象
Subject realSubject = new RealSubject();
// 創建處理器,綁定真實對象
InvocationHandler handler = new DynamicProxy(realSubject);
// 動態生成代理對象
Subject subject = (Subject) Proxy.newProxyInstance(
handler.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
handler
);
// 驗證代理類名稱格式:$Proxy+數字
System.out.println(subject.getClass().getName()); // 輸出:$Proxy0
// 通過代理調用業務方法
subject.rent();
subject.hello("world");
}
}
控制台輸出與關鍵現象分析
$Proxy0
before rent house
Method:public abstract void com.xiaoluo.dynamicproxy.Subject.rent()
I want to rent my house
after rent house
before rent house
Method:public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)
hello: world
after rent house
關鍵現象解讀
- 代理類名稱:
$Proxy0 — JVM 運行時動態生成,**非** InvocationHandler 類型,**非** 業務接口類型,命名規則固定
- 強制轉型成功原因:
newProxyInstance 第二參數傳入 Subject.class,代理類**實現**了該接口,故可轉型
- 方法調用流向:代理對象方法調用 → JVM 攔截 → 委派
handler.invoke() → 反射調用真實對象 → 返回結果
- Method 對象:精確對應接口聲明的方法簽名,包含完整元數據
動態代理 vs 靜態代理 對比
| 維度 | 靜態代理 | 動態代理 |
| 代理類生成時間 | 編譯期 | 運行期 |
| 代理類數量 | 每個目標類一個 | 單一通用處理器服務多類 |
| 接口變更影響 | 需修改代理類 | 無需修改,自動適配 |
| 適用場景 | 固定、少量代理 | 框架級、批量、AOP |
進階應用:Spring AOP 底層映射
- Pointcut → 篩選
method 參數
- Advice →
invoke() 中的前置/後置/環繞邏輯
- Target →
subject 真實對象
- Proxy →
Proxy.newProxyInstance() 生成的代理對象
最佳實踐與避坑指南
- **只能代理接口**:JDK 動態代理要求目標類實現至少一個接口;需代理類請用 CGLIB/ByteBuddy
- **避免在 invoke 中調用 proxy 同名方法** → 無限遞歸 → StackOverflowError
- **性能優化**:緩存
Method 對象、使用 MethodHandle、考慮字節碼生成框架
- **序列化支援**:代理類實現
Serializable 需額外處理 writeReplace
- 上一篇文章: 《Thinking in Java》讀書筆記第 11 章:持有你的對象 — 容器框架深度解析
- 下一篇文章: JVM 核心架構深度解析:運行時數據區、類加載、記憶體模型全景圖
Copyright ©
程式碼之家 All Rights Reserved