buyMeat(money) -> meat。代理類由程式設計師手寫,編譯期確定。
interface Trader { int buyMeat(int money); }
class Farmer implements Trader {
public int buyMeat(int money) { return money * 2; } // 1元買2斤
}
class Butcher implements Trader {
private final Farmer farmer = new Farmer();
public int buyMeat(int money) {
int meat = farmer.buyMeat(money);
return meat + 5; // 注水
}
}
class You {
public void work() {
Trader trader = new Butcher(); // 可換 new Farmer()
int meat = trader.buyMeat(10);
System.out.println("得到肉:" + meat + " 斤");
}
}
缺點:每個目標類都需手寫代理類,重複代碼多,維護成本高。
執行期透過 java.lang.reflect.Proxy 自動生成代理類,核心邏輯集中在 InvocationHandler。
public interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
proxy:代理物件本身(極少直接使用)。method:正被調用的方法對象(反射)。args:呼叫參數陣列。
import java.lang.reflect.*;
public class DynamicProxyDemo {
public static void main(String[] args) {
// 目標物件
Trader farmer = new Farmer();
// 代理邏輯
InvocationHandler handler = (proxy, method, args) -> {
System.out.println("[代理] 即將調用:" + method.getName());
Object result = method.invoke(farmer, args); // 轉發給目標
if (method.getName().equals("buyMeat") && result instanceof Integer) {
result = (Integer) result + 5; // 增強:注水
}
System.out.println("[代理] 調用完成,結果:" + result);
return result;
};
// 生成代理物件
Trader proxy = (Trader) Proxy.newProxyInstance(
Trader.class.getClassLoader(),
new Class[]{Trader.class},
handler
);
// 客戶端調用
int meat = proxy.buyMeat(10);
System.out.println("最終得到:" + meat + " 斤"); // 25
}
}
interface Trader { int buyMeat(int money); }
class Farmer implements Trader {
public int buyMeat(int money) { return money * 2; }
}
| 維度 | 靜態代理 | 動態代理 |
|---|---|---|
| 代理類來源 | 手寫 .java | 執行期字節碼生成 |
| 適用場景 | 固定、少量目標類 | 大量類、通用橫切邏輯 |
| 效能 | 直接調用,最快 | 反射調用,稍慢 (可用 MethodHandle 優化) |
| 維護性 | 分散 | 集中在 InvocationHandler |
@Transactional、@Cacheable、日誌、權限檢查皆由動態代理織入。LazyInitialization、MyBatis 結果映射代理。JDK 動態代理僅支援介面。代理具體類需用 CGLIB(繼承子類覆寫方法)或 ByteBuddy(更強大的字節碼操作庫)。Spring 預設:介面用 JDK,類用 CGLIB。