代理模式為目標對象提供代理,控制訪問並可增強功能。分為靜態代理(手寫)、JDK 動態代理(介面)、CGLIB 動態代理(繼承類)。本文解析三種形式核心原理、代碼實現、優缺點與 Spring AOP 選型邏輯。
// 介面
public interface UserDao { void save(); void delete(); }
// 真實對象
public class UserDaoImpl implements UserDao {
public void save() { System.out.println("保存數據..."); }
public void delete() { System.out.println("刪除數據..."); }
}
// 代理對象(同包/目錄)
public class UserDaoProxy implements UserDao {
private UserDao target;
public UserDaoProxy(UserDao target) { this.target = target; }
public void save() {
System.out.println("開啟事務");
target.save();
System.out.println("提交事務");
}
public void delete() {
System.out.println("開啟事務");
target.delete();
System.out.println("提交事務");
}
}
缺點:每個目標類需手寫一個代理類,代碼冗餘極大,維護成本高。
使用:
UserDao proxy = (UserDao) JdkProxyFactory.createProxy(new UserDaoImpl());
限制:目標類**必須實現介面**,代理通過介面多態調用。3. CGLIB 動態代理:基於繼承
使用:
UserDao proxy = (UserDao) CglibProxyFactory.createProxy(UserDaoImpl.class);
原理:生成目標類子類,重寫非 final 方法。
限制:無法代理 `final` 類/方法、`private` 方法;需引入 `cglib` 依賴。三種代理對照
| 維度 | 靜態代理 | JDK 動態代理 | CGLIB 動態代理 |
|---|---|---|---|
| 目標類要求 | 任意 | 必須實現介面 | 非 final 類、非 final 方法 |
| 代理生成 | 編譯期手寫 | 運行期反射生成介面實現類 | 運行期字節碼生成子類 |
| 性能(JDK8+) | 最快 | 優化後接近直接調用 | FastClass 機制,略快於早期 JDK |
| 適用場景 | 極少數固定增強 | 標準介面編程、Spring AOP 默認 | 無介面類、第三方庫、性能敏感 |
// 簡化版 ProxyFactory 邏輯
if (targetClass.isInterface() || proxyTargetClass == false) {
return new JdkDynamicAopProxy(config); // 默認優先 JDK
} else {
return new ObjenesisCglibAopProxy(config); // 強制/無介面用 CGLIB
}