動態代理是 Java 進階開發中的核心技術,廣泛應用於 Spring AOP、事務管理、日誌攔截等場景。理解其原理前,先釐清靜態代理:由程式設計師預先編寫代理類,編譯期即確定,一個代理類只能服務一個介面,擴展性極差。
// 介面
public interface Account {
void queryAccount();
void updateAccount();
}
// 目標實現
public class AccountImpl implements Account {
public void queryAccount() { System.out.println("查看賬戶"); }
public void updateAccount() { System.out.println("修改賬戶"); }
}
// 靜態代理類
public class AccountProxy implements Account {
private Account account;
public AccountProxy(Account account) { this.account = account; }
public void queryAccount() {
System.out.println("代理before");
account.queryAccount();
System.out.println("代理after");
}
public void updateAccount() {
System.out.println("代理before");
account.updateAccount();
System.out.println("代理after");
}
}
核心類:java.lang.reflect.Proxy 與 InvocationHandler。限制:只能代理實現介面的類。
public class AccountProxyFactory implements InvocationHandler {
private Object target;
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!method.getDeclaringClass().getName().equals("java.lang.Object")) {
System.out.println("代理before");
}
Object result = method.invoke(target, args);
if (!method.getDeclaringClass().getName().equals("java.lang.Object")) {
System.out.println("代理after");
}
return result;
}
}
使用方式:Account account = (Account) new AccountProxyFactory().bind(new AccountImpl());
針對 JDK 只能代理介面的缺陷,CGLIB 通過繼承目標類、覆寫父類方法實現代理,無需實現介面。依賴 asm 與 cglib 套件。
public class AccountCglibProxyFactory implements MethodInterceptor {
private Object target;
public Object getInstance(Object target) {
this.target = target;
return Enhancer.create(target.getClass(), this);
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (!method.getDeclaringClass().getName().equals("java.lang.Object")) {
System.out.println("before");
}
Object result = proxy.invokeSuper(obj, args); // 必須用 invokeSuper 避免死循環
if (!method.getDeclaringClass().getName().equals("java.lang.Object")) {
System.out.println("after");
}
return result;
}
}
使用方式:Account account = (Account) new AccountCglibProxyFactory().getInstance(new AccountImpl());
甚至可代理無介面的普通類:Person person = (Person) new AccountCglibProxyFactory().getInstance(new Person());
| 特性 | JDK 動態代理 | CGLIB 動態代理 |
|---|---|---|
| 代理機制 | 介面實現(組合) | 繼承子類(繼承) |
| 目標類要求 | 必須實現一個或多個介面 | 無需介面,不能為 final 類/方法 |
| 效能 | 生成代理快,調用稍慢 | 生成代理較慢(字節碼操作),調用快 |
| 適用場景 | Spring AOP 預設(有介面時) | 無介面類、第三方庫類增強 |
proxy-target-class=true,預設使用 CGLIB,可透過配置調整。