// 錯誤版 InvocationHandler
public class TimeInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("startTime: " + System.currentTimeMillis());
Object obj = method.invoke(proxy, args); // 關鍵錯誤:傳入 proxy 而非 target
System.out.println("endTime: " + System.currentTimeMillis());
return obj;
}
}
// 測試代碼
TimeInvocationHandler handler = new TimeInvocationHandler();
UserService proxy = (UserService) Proxy.newProxyInstance(
UserServiceImpl.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(),
handler
);
proxy.update(2); // 觸發無限遞歸 -> StackOverflowError 包裝為 UndeclaredThrowableException
JDK 動態代理生成的 $Proxy0 類繼承 Proxy 並實現目標接口。其核心邏輯:
// Proxy 類簡化結構
protected InvocationHandler h;
protected Proxy(InvocationHandler h) { this.h = h; }
// 生成的代理方法(如 update)
public void update(int id) {
h.invoke(this, methodOfUpdate, new Object[]{id}); // this = 代理對象實例
}
當 invoke 內部調用 method.invoke(proxy, args) 時,又會觸發代理的 update 方法,形成無限遞歸。
// 正確版:構造器注入目標對象
public class TimeInvocationHandler implements InvocationHandler {
private final Object target;
public TimeInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("startTime: " + System.currentTimeMillis());
Object result = method.invoke(target, args); // 關鍵修正:調用目標對象
System.out.println("endTime: " + System.currentTimeMillis());
return result;
}
}
// 測試代碼修正
TimeInvocationHandler handler = new TimeInvocationHandler(new UserServiceImpl());
UserService proxy = (UserService) Proxy.newProxyInstance(
UserServiceImpl.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(),
handler
);
UserServiceImpl target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("startTime: " + System.currentTimeMillis());
Object result = method.invoke(target, args);
System.out.println("endTime: " + System.currentTimeMillis());
return result;
}
}
);
| 參數 | 含義 | 關鍵點 |
|---|---|---|
proxy | 代理實例本身($Proxy0) | 僅供標識,不可用於方法調用 |
method | 接口方法的 Method 對象 | 聲明類為接口,非實現類 |
args | 調用參數數組 | 基本類型已自動裝箱 |
// 接口定義
public interface UserService {
int save(); // 返回 int
void update(int id); // 返回 void
}
// 如果 invoke 返回 null
public Object invoke(...) { return null; }
proxy.update(2); // 正常:void 方法忽略返回值
proxy.save(); // 拋出 NullPointerException:JVM 無法將 null 轉為 int
結論:invoke 必須返回與接口方法聲明兼容的值,基本類型返回值不能為 null。
method.invoke(target, args) 而非 method.invoke(proxy, args)proxy 僅作標識,不可用於反射調用