代理 為委託類(被代理類)提供替身,兩大價值:
| 維度 | 靜態代理 | 動態代理 |
|---|---|---|
| 代理類來源 | 編譯期手寫 .java | 執行期 JVM 生成 $Proxy0.class |
| 方法增強 | 每個方法手工加邏輯 | 統一 InvocationHandler 攔截 |
| 維護成本 | 介面方法多時極高 | 極低,只需改處理器邏輯 |
| 適用場景 | 固定少量增強點 | AOP、框架級通用增強 |
┌─────────────┐ ┌──────────────────┐ ┌─────────────┐
│ Client │────▶│ $Proxy0 │────▶│ InvocationHandler │
│ (呼叫端) │ │ (動態生成代理) │ │ (攔截器) │
└─────────────┘ └──────────────────┘ └──────┬──────┘
│
▼
┌─────────────────┐
│ Target Object │
│ (真實委託類) │
└─────────────────┘
invoke(proxy, method, args),統一攔截public interface UserService {
void addUser(String name);
User getUser(Long id);
}
public class UserServiceImpl implements UserService {
public void addUser(String name) { System.out.println("新增用戶: " + name); }
public User getUser(Long id) { return new User(id, "User" + id); }
}
public class LoggingHandler implements InvocationHandler {
private final Object target;
public LoggingHandler(Object target) { this.target = target; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("[前置] " + method.getName() + " 參數: " + Arrays.toString(args));
Object result = method.invoke(target, args); // 呼叫真實對象
System.out.println("[後置] " + method.getName() + " 回傳: " + result);
return result;
}
}
public class ProxyDemo {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new LoggingHandler(target)
);
proxy.addUser("Alice");
System.out.println("---");
System.out.println(proxy.getUser(1L));
}
}
public final class $Proxy0 extends Proxy implements UserService {
private static Method m_addUser, m_getUser, m_equals, m_toString, m_hashCode;
public $Proxy0(InvocationHandler h) { super(h); }
public void addUser(String name) {
super.h.invoke(this, m_addUser, new Object[]{name});
}
public User getUser(Long id) {
return (User) super.h.invoke(this, m_getUser, new Object[]{id});
}
// equals/toString/hashCode 同理轉發給 handler
static {
m_addUser = UserService.class.getMethod("addUser", String.class);
m_getUser = UserService.class.getMethod("getUser", Long.class);
// ...
}
}
關鍵點:
java.lang.reflect.Proxy,持有 InvocationHandler hh.invoke(this, method, args)Method 物件,避免重複查找| 特性 | JDK Proxy | CGLIB |
|---|---|---|
| 前提條件 | 目標類實作介面 | 無需介面,繼承目標類 |
| 生成方式 | 實作介面 | 繼承子類(FastClass 機制) |
| final 方法 | 正常代理 | 無法代理(無法覆寫) |
| 效能(JDK 8+) | 極佳(invoke 擊穿優化) | 略快但差距微小 |
| Spring 預設 | 有介面用 JDK | 無介面/強制 proxy-target-class=true |
invoke 做耗時操作,影響所有被代理方法@Transactional 等註解驅動的代理 vs 手工 Proxy.newProxyInstance-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true 輸出 $Proxy*.class