代理模式為目標對象提供替身,控制對其訪問,核心角色:Subject(抽象接口)、RealSubject(真實對象)、Proxy(代理類)。本文系統對比三種實現方式,深入源碼級原理,附完整可運行代碼。
// 1. 抽象接口
public interface IAnimal { void bark(); }
// 2. 真實對象
public class Dog implements IAnimal {
private String name;
public Dog(String name) { this.name = name; }
public void bark() { System.out.println(name + " bark: wang wang..."); }
}
// 3. 代理類
public class DogProxy implements IAnimal {
private Dog dog;
public DogProxy(Dog dog) { this.dog = dog; }
public void bark() {
long t = System.currentTimeMillis();
System.out.println("dog will bark...");
dog.bark();
System.out.println("dog barked, cost " + (System.currentTimeMillis()-t) + "ms");
}
}
// 4. 使用
IAnimal dog = new Dog("大黃");
IAnimal proxy = new DogProxy(dog);
proxy.bark();
java.lang.reflect.Proxy:生成代理類的工廠java.lang.reflect.InvocationHandler:攔截方法調用的核心接口// 1. 實現 InvocationHandler
public class DogProxyHandler implements InvocationHandler {
private Object target;
public DogProxyHandler(Object target) { this.target = target; }
@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;
}
}
// 2. 生成代理並調用
IAnimal dog = new Dog("大黃");
InvocationHandler handler = new DogProxyHandler(dog);
IAnimal proxy = (IAnimal) Proxy.newProxyInstance(
dog.getClass().getClassLoader(),
dog.getClass().getInterfaces(),
handler
);
proxy.bark();
proxy class: class com.sun.proxy.$Proxy0 before: bark 大黃 bark: wang wang... after: bark
public final class $Proxy0 extends Proxy implements IAnimal {
protected $Proxy0(InvocationHandler h) { super(h); }
public final void bark() {
super.h.invoke(this, m3, null); // m3 = IAnimal.bark Method 對象
}
// equals/hashCode/toString 同理委派給 InvocationHandler
static {
m3 = Class.forName("...IAnimal").getMethod("bark");
}
}
關鍵點:
Proxy、實現目標所有接口InvocationHandler.invoke()Method 對象提升性能<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
net.sf.cglib.proxy.Enhancer:字節碼增強器,生成子類MethodInterceptor:方法攔截器(對標 InvocationHandler)CallbackFilter:按方法名決定使用哪個 Callback// 目標類(無接口)
public class Animal {
public void bark() { System.out.println("i am 大黃!"); }
public String singSong(String name) { return name + " is singing!"; }
public String testt(String name) { return name + " is testing!"; }
}
// 1. 通用攔截器:前後加日志
public class AnimalWrapper implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("BEFORE");
Object result = proxy.invokeSuper(obj, args); // 調用父類方法
System.out.println("AFTER");
return result;
}
}
// 2. 過濾器:方法名長度為 5 的才增強 (bark=4, singSong=8, testt=5)
public class ApiFilter implements CallbackFilter {
@Override public int accept(Method method) {
return method.getName().length() == 5 ? 1 : 0; // 1 用 Callback[1]=NoOp
}
}
// 3. 組裝與測試
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Animal.class);
enhancer.setCallbacks(new Callback[]{new AnimalWrapper(), NoOp.INSTANCE}); // 0:增強, 1:無操作
enhancer.setCallbackFilter(new ApiFilter());
Animal proxy = (Animal) enhancer.create();
proxy.bark(); // BEFORE ... AFTER
proxy.singSong("老劉"); // BEFORE ... AFTER
proxy.testt("大黃"); // 直接輸出:大黃 is testing! (無增強)
public class Animal$$EnhancerByCGLIB$$223151cf extends Animal implements Factory {
private MethodInterceptor CGLIB$CALLBACK_0;
private NoOp CGLIB$CALLBACK_1;
private static CallbackFilter CGLIB$CALLBACK_FILTER;
public final void bark() {
CGLIB$CALLBACK_0.intercept(this, CGLIB$bark$0$Method, CGLIB$emptyArgs, CGLIB$bark$0$Proxy);
}
public final String testt(String s) {
CGLIB$CALLBACK_1.intercept(...); // NoOp 直接調用父類
}
// FastClass 機制:索引式方法調用,比反射快
}
switch(index) 直接分發,避免反射開銷CallbackFilter)| 維度 | 靜態代理 | JDK 動態代理 | CGLIB 動態代理 |
|---|---|---|---|
| 目標要求 | 任意類 | 必須實現接口 | 非 final 類、非 final 方法 |
| 生成方式 | 編譯期手寫 | 運行時生成實現接口的類 | 運行時生成子類 |
| 性能 | 最優 (直接調用) | 反射調用,JDK 8+ 優化明顯 | FastClass 索引調用,通常最快 |
| 適用場景 | 固定、少量目標 | 接口驅動、框架級 AOP (Spring AOP 默認) | 無接口類、第三方庫、高性能場景 |
| Spring 選擇策略 | - | 目標實現接口 → JDK | 目標無接口或強制 proxy-target-class=true → CGLIB |
spring.aop.proxy-target-class=true(全 CGLIB),可配置為 false 使用 JDKinvoke/intercept 中執行耗時操作