代理模式三大核心角色:
java.lang.reflect.Proxy、java.lang.reflect.InvocationHandlerInvocationHandler.invoke()package www.csdn.spring.jdk.proxy;
public interface SayGoodBye {
void say(String content);
}
package www.csdn.spring.jdk.proxy;
public class SayGoodByeImpl implements SayGoodBye {
@Override
public void say(String content) {
System.out.println("say: " + content);
}
}
package www.csdn.spring.jdk.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxy implements InvocationHandler {
private Object target; // 真實主題引用
// 工廠方法:生成代理實例
public Object createProxyInstance(Object target) {
this.target = target;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 1. 類加載器
target.getClass().getInterfaces(), // 2. 實現的接口列表
this // 3. 調用處理器
);
}
// 核心攔截邏輯
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue = null;
beforeMethod(); // 前置增強
returnValue = method.invoke(target, args); // 調用真實方法
afterMethod(); // 後置增強
return returnValue;
}
private void beforeMethod() {
System.out.println("--------------------- 目標方法之前執行");
}
private void afterMethod() {
System.out.println("--------------------- 目標方法之後執行");
}
}
package www.csdn.spring.jdk.proxy;
import org.junit.Test;
public class TestSay {
@Test
public void say() {
// 真實主題
SayGoodByeImpl realSubject = new SayGoodByeImpl();
// 創建代理
JDKProxy jdkProxy = new JDKProxy();
SayGoodBye proxySubject = (SayGoodBye) jdkProxy.createProxyInstance(realSubject);
// 通過代理調用
proxySubject.say("你和他分手吧!!!");
}
}
--------------------- 目標方法之前執行
say: 你和他分手吧!!!
--------------------- 目標方法之後執行
| 參數 | 含義 | 獲取方式 |
|---|---|---|
| ClassLoader | 代理類加載器 | target.getClass().getClassLoader() |
| Class[] interfaces | 代理實現的接口列表 | target.getClass().getInterfaces() |
| InvocationHandler | 方法調用分發器 | 自定義實現類 |
proxy:代理對象自身引用(極少直接使用)method:被調用的接口方法 Method 對象args:調用參數數組public final class $Proxy0 extends Proxy implements SayGoodBye {
protected $Proxy0(InvocationHandler h) { super(h); }
public final void say(String content) {
super.h.invoke(this, m1, new Object[]{content}); // m1 為 say 方法 Method 對象
}
// equals、hashCode、toString 也被代理(來自 Object)
}
| 維度 | JDK 動態代理 | CGLIB |
|---|---|---|
| 前提條件 | 目標類實現接口 | 無需接口,繼承目標類 |
| 代理類型 | 接口代理(實現接口) | 類代理(生成子類) |
| final 方法 | 接口無 final 方法 | 無法代理 final 方法 |
| 性能(歷史) | 早期較慢,JDK 8+ 優化顯著 | 早期較快,現代 JDK 差異微小 |
| Spring 默認策略 | 有接口優先 JDK | 無接口或 proxy-target-class="true" |
Spring AOP 默認策略:
<aop:config proxy-target-class="true"> 或 @EnableAspectJAutoProxy(proxyTargetClass = true)最佳實踐:面向接口編程,優先使用 JDK 動態代理,獲得更鬆耦合的架構;僅在無法實現接口(第三方類、遺留代碼)時退而求其次使用 CGLIB。