代理模式是結構型設計模式的核心,通過代理對象控制對真實對象的訪問,可在不修改原類碼前提下增強功能(日誌、事務、權限、緩存等)。Spring AOP 底層即基於動態代理實現。本文從概念到代碼,徹底講透三種代理實現方式。
| 角色 | 說明 | 對應代碼 |
|---|---|---|
| Subject (主題接口) | 定義代理與真實對象的公共方法 | Factory 接口 |
| RealSubject (真實主題) | 實際業務邏輯實現者 | NikeFactory |
| Proxy (代理類) | 持有真實對象引用,控制訪問並可添加橫切邏輯 | NikeFactoryProxy / 動態代理類 |
| Client (客戶端) | 通過代理調用業務方法 | Main / Test 類 |
特點:代理類在編譯期就確定,一個真實類對應一個代理類,代碼冗餘高。
public interface Factory {
void say();
void produce();
}
public class NikeFactory implements Factory {
@Override
public void say() {
System.out.println("我要開始生產了!");
}
@Override
public void produce() {
System.out.println("正在生產 Nike 衣服!");
}
}
public class NikeFactoryProxy implements Factory {
private final NikeFactory nikeFactory = new NikeFactory();
@Override
public void say() {
before();
nikeFactory.say();
after();
}
@Override
public void produce() {
before();
nikeFactory.produce();
after();
}
private void before() {
System.out.println("在正式開始生產之前,我要說兩句!");
}
private void after() {
System.out.println("大家做的不錯!");
}
}
@Test
public void testStaticProxy() {
Factory proxy = new NikeFactoryProxy();
proxy.say();
proxy.produce();
}
/* 輸出:
在正式開始生產之前,我要說兩句!
我要開始生產了!
大家做的不錯!
在正式開始生產之前,我要說兩句!
正在生產 Nike 衣服!
大家做的不錯!
*/
核心機制:運行時通過 java.lang.reflect.Proxy 生成實現目標接口的匿名類,**前提:目標類必須實現一個或多個接口**。
@Test
public void testJDKProxy() {
Factory proxy = (Factory) Proxy.newProxyInstance(
NikeFactory.class.getClassLoader(),
NikeFactory.class.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【JDK動態代理】before: " + method.getName());
Object result = method.invoke(new NikeFactory(), args);
System.out.println("【JDK動態代理】after: " + method.getName());
return result;
}
}
);
proxy.say();
proxy.produce();
}
public class DynamicProxy implements InvocationHandler {
private final Object target;
public DynamicProxy(Object target) {
this.target = target;
}
@SuppressWarnings("unchecked")
public T getProxy() {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【通用代理】before: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("【通用代理】after: " + method.getName());
return result;
}
}
// 使用
@Test
public void testJDKProxy2() {
Factory proxy = new DynamicProxy(new NikeFactory()).getProxy();
proxy.say();
proxy.produce();
}
核心機制:通過繼承目標類、重寫非 final 方法實現代理,**無需目標類實現接口**;底層使用 ASM 字節碼操作框架。需額外引入依賴:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
public class CglibProxy implements MethodInterceptor {
private static final CglibProxy INSTANCE = new CglibProxy();
private CglibProxy() {}
public static CglibProxy getInstance() {
return INSTANCE;
}
@SuppressWarnings("unchecked")
public T getProxy(Class cls) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(cls);
enhancer.setCallback(this);
return (T) enhancer.create();
}
@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;
}
}
// 使用
@Test
public void testCglibProxy2() {
NikeFactory proxy = CglibProxy.getInstance().getProxy(NikeFactory.class);
proxy.say();
proxy.produce();
}
| 維度 | 靜態代理 | JDK 動態代理 | CGLIB 動態代理 |
|---|---|---|---|
| 目標類要求 | 無 | 必須實現接口 | 無(但不能代理 final 類/方法) |
| 生成時機 | 編譯期 | 運行時 | 運行時 |
| 代理類關係 | 實現同一接口 | 實現同一接口 | 繼承目標類 |
| 性能(調用) | 最快(直接調用) | 反射調用,稍慢 | FastClass 機制,較 JDK 快 |
| 適用場景 | 簡單、固定增強 | 接口驅動、框架級 AOP | 無接口類、第三方庫增強 |
@EnableAspectJAutoProxy(proxyTargetClass = true) 或 aop:config proxy-target-class="true"。proxyTargetClass = true,優先 CGLIB。InvocationHandler 與 MethodInterceptor 兩大核心攔截接口,即掌握了 AOP 的靈魂。