代理模式為目標對象提供一個替身,控制對其訪問,可在不修改目標代碼前提下增強功能(日誌、事務、權限、緩存等)。分為靜態代理(編譯期確定)與動態代理(運行時生成)。
package com.zj.proxy;
public interface Subject {
void operation1();
void operation2(String arg);
}
package com.zj.proxy;
public class RealSubject implements Subject {
@Override
public void operation1() {
System.out.println("RealSubject: 執行 operation1");
}
@Override
public void operation2(String arg) {
System.out.println("RealSubject: 執行 operation2 參數=" + arg);
}
}
package com.zj.proxy;
public class ProxySubject implements Subject {
private final Subject target; // 被代理對象
public ProxySubject(Subject target) {
this.target = target;
}
@Override
public void operation1() {
System.out.println("Proxy: 前置增強 - operation1");
target.operation1();
System.out.println("Proxy: 後置增強 - operation1");
}
@Override
public void operation2(String arg) {
System.out.println("Proxy: 前置增強 - operation2, arg=" + arg);
target.operation2(arg);
System.out.println("Proxy: 後置增強 - operation2");
}
}
package com.zj.proxy.client;
import com.zj.proxy.*;
public class StaticProxyDemo {
public static void main(String[] args) {
Subject real = new RealSubject();
System.out.println("=== 直接調用 ===");
real.operation1();
real.operation2("ZJ");
System.out.println("\n=== 代理調用 ===");
Subject proxy = new ProxySubject(real);
proxy.operation1();
proxy.operation2("ZJ");
}
}
=== 直接調用 ===
RealSubject: 執行 operation1
RealSubject: 執行 operation2 參數=ZJ
=== 代理調用 ===
Proxy: 前置增強 - operation1
RealSubject: 執行 operation1
Proxy: 後置增強 - operation1
Proxy: 前置增強 - operation2, arg=ZJ
RealSubject: 執行 operation2 參數=ZJ
Proxy: 後置增強 - operation2
核心類:java.lang.reflect.Proxy、InvocationHandler
package com.zj.proxy.dynamic;
import java.lang.reflect.*;
public class DynamicProxyHandler implements InvocationHandler {
private final Object target;
public DynamicProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("=== 動態代理攔截 ===");
System.out.println("代理類: " + proxy.getClass().getName());
System.out.println("方法: " + method.getName());
System.out.println("參數: " + Arrays.toString(args));
// 前置增強
long start = System.nanoTime();
Object result = method.invoke(target, args); // 調用目標方法
// 後置增強
System.out.println("耗時: " + (System.nanoTime() - start) + " ns");
return result;
}
}
package com.zj.proxy.client;
import java.lang.reflect.Proxy;
import com.zj.proxy.*;
import com.zj.proxy.dynamic.DynamicProxyHandler;
public class DynamicProxyDemo {
public static void main(String[] args) {
Subject real = new RealSubject();
// 生成代理實例
Subject proxy = (Subject) Proxy.newProxyInstance(
Subject.class.getClassLoader(), // 類加載器
new Class[] { Subject.class }, // 實現的接口列表
new DynamicProxyHandler(real) // 處理器
);
System.out.println("代理類名稱: " + proxy.getClass().getName());
// 輸出: com.sun.proxy.$Proxy0
proxy.operation1();
proxy.operation2("Dynamic");
}
}
代理類名稱: com.sun.proxy.$Proxy0
=== 動態代理攔截 ===
代理類: com.sun.proxy.$Proxy0
方法: operation1
參數: []
RealSubject: 執行 operation1
耗時: 12345 ns
=== 動態代理攔截 ===
代理類: com.sun.proxy.$Proxy0
方法: operation2
參數: [Dynamic]
RealSubject: 執行 operation2 參數=Dynamic
耗時: 6789 ns
| 維度 | 靜態代理 | JDK 動態代理 |
|---|---|---|
| 代理類生成時間 | 編譯期 | 運行時 (字節碼生成) |
| 目標類要求 | 實現接口 | 必須實現一個或多個接口 |
| 代碼復用性 | 低 (每個目標類需寫代理類) | 高 (一個 Handler 處理所有接口) |
| 方法攔截粒度 | 手動控制每個方法 | 統一在 invoke() 中處理 |
| 性能開銷 | 極低 (直接調用) | 較高 (反射調用、方法查找) |
| 調試難度 | 易 (代碼可見) | 難 (動態生成類不可見) |
JDK 動態代理要求目標實現接口。若目標是普通類,需用 CGLIB(繼承子類重寫方法):
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
// 使用示例
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class); // 針對類而非接口
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
System.out.println("CGLIB 前置增強");
Object result = proxy.invokeSuper(obj, args);
System.out.println("CGLIB 後置增強");
return result;
});
RealSubject proxy = (RealSubject) enhancer.create();
Spring 根據目標類特性自動選擇:
proxy-target-class=true → CGLIB@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 強制 CGLIB
public class AopConfig {}