代理模式:為目標對象提供一個代理對象,控制對目標對象的訪問,在不修改目標代碼前提下增強功能。
| 角色 | 說明 |
|---|---|
| Subject (主題接口) | 定義代理和目標對象的公共方法契約 |
| RealSubject (真實主題) | 實際業務邏輯實現者 |
| Proxy (代理類) | 持有 RealSubject 引用,實現 Subject,控制訪問、添加橫切邏輯 |
| Client (客戶端) | 通過 Subject 接口調用 Proxy,無感知 RealSubject |
// 1. 主題接口
public interface IDBQuery { String request(); }
// 2. 真實主題 (昂貴資源初始化)
public class DBQuery implements IDBQuery {
public DBQuery() { Thread.sleep(1000); /* 模擬建立連接 */ }
@Override public String request() { return "真實數據"; }
}
// 3. 代理類 (延遲加載)
public class DBQueryProxy implements IDBQuery {
private DBQuery real;
@Override public synchronized String request() {
if (real == null) real = new DBQuery(); // 首次調用才創建
return real.request();
}
}
// 4. 客戶端
public class Main { public static void main(String[] args) {
IDBQuery proxy = new DBQueryProxy();
System.out.println(proxy.request()); // 首次耗時 ~1s
System.out.println(proxy.request()); // 後續極快
}}
痛點: 每個接口需手寫一個代理類,接口變更雙方同步修改,維護成本高。
public class JDKProxyHandler implements InvocationHandler {
private final Object target;
public JDKProxyHandler(Object target) { this.target = target; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【JDK代理】前置增強: " + method.getName());
Object result = method.invoke(target, args); // 反射調用目標
System.out.println("【JDK代理】後置增強");
return result;
}
public static Object newProxyInstance(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(), // 必須實現接口
new JDKProxyHandler(target));
}
}
public class CglibProxy implements MethodInterceptor {
private final Object target;
public CglibProxy(Object target) { this.target = target; }
@Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("【CGLIB】事務開啟");
Object result = proxy.invokeSuper(obj, args); // 調用父類方法
System.out.println("【CGLIB】事務提交");
return result;
}
public static Object newInstance(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new CglibProxy(target));
return enhancer.create();
}
}
| 維度 | JDK Proxy | CGLIB |
|---|---|---|
| 前提條件 | 目標類必須實現一個或多個接口 | 無需接口,通過繼承子類代理 |
| 核心機制 | 生成實現接口的匿名類 | 生成目標類的子類,重寫方法 |
| final 方法 | 可代理 | 無法代理 (無法重寫) |
| 性能 (調用) | 反射調用,稍慢 | FastClass 機制,直接索引調用,較快 |
| Spring 默認策略 | 有接口用 JDK,無接口用 CGLIB | 可強制 proxyTargetClass=true 全用 CGLIB |
Spring AOP 本質是 動態代理:
@Aspect 切面類,解析 @Pointcut、@Before/@After/@Around。Advisor (切面 + 切點)。AnnotationAwareAspectJAutoProxyCreator 在 Bean 初始化後,判斷是否匹配切點。MethodInterceptor 鏈 → 織入通知邏輯 → 反射/調用父類執行目標方法。// 目標類 (無接口)
public class UserService {
public void save(User u) { System.out.println("保存用戶: " + u.getName()); }
public void delete(int id) { System.out.println("刪除用戶: " + id); }
}
// 切面
@Aspect
@Component
public class TxAspect {
@Around("execution(* *.save(..)) || execution(* *.delete(..))")
public Object manageTx(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("=== 開啟事務 ===");
try {
Object ret = pjp.proceed(); // 執行目標方法
System.out.println("=== 提交事務 ===");
return ret;
} catch (Exception e) {
System.out.println("=== 回滾事務 ===");
throw e;
}
}
}
// 啟動類
@EnableAspectJAutoProxy(proxyTargetClass = true) // 強制 CGLIB
@SpringBootApplication
public class App { public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(App.class, args);
UserService svc = ctx.getBean(UserService.class);
svc.save(new User("張三")); // 控制台輸出:開啟事務 -> 保存用戶 -> 提交事務
}}
代理模式是框架級開發的基石。掌握「靜態代理理解本質 → JDK 動態代理掌握接口代理 → CGLIB 掌握類代理 → Spring AOP 理解自動化織入」的遞進鏈路,能讓你在排查事務不生效、@Async 失效、循環依賴代理提前暴露等疑難雜症時,直擊字節碼層面本質。