動態代理解決靜態代理的程式碼冗餘問題:代理類在執行期動態生成,單一 InvocationHandler 統一處理所有方法攔截,無需為每個目標類寫專屬代理類。
java.lang.reflect.Proxy:提供 newProxyInstance() 產生代理物件。java.lang.reflect.InvocationHandler:介面,唯一方法 invoke(Object proxy, Method method, Object[] args) 定義攔截邏輯。package aop_002;
public interface G3Student {
void studyAtHome(String core);
void studyAtSchool(String core);
}
package aop_002;
public class G3Student_boy implements G3Student {
@Override
public void studyAtHome(String core) {
System.out.println(core + "在 HOME 學習");
}
@Override
public void studyAtSchool(String core) {
System.out.println(core + "在 SCHOOL 學習");
}
}
package aop_002;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class G3Student_handler implements InvocationHandler {
private final Object target; // 持有目標物件(任意型別)
public G3Student_handler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before(); // 前置增強
Object result = method.invoke(target, args); // 委派目標方法
after(); // 後置增強
return result;
}
private void before() {
System.out.println("代理:準備買菜");
System.out.println("代理:準備洗菜");
System.out.println("代理:準備炒菜");
System.out.println("代理:準備煮飯");
System.out.println("-----------------");
}
private void after() {
System.out.println("-----------------");
System.out.println("代理:購買補品");
System.out.println("代理:熬製補品");
System.out.println("代理:加入佐料");
System.out.println();
}
}
package aop_002;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
G3Student target = new G3Student_boy(); // 目標物件
InvocationHandler handler = new G3Student_handler(target);
// 動態生成代理物件
G3Student proxy = (G3Student) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
proxy.studyAtSchool("小明");
proxy.studyAtHome("小明");
}
}
Object target:不綁定具體介面,同一 Handler 可代理 G3Student_boy、G3Student_girl 等任意實作 G3Student 的類別。method.invoke(target, args):反射呼叫目標方法,method 由 JVM 自動傳入(被呼叫的方法),args 為實際參數。無需在 Handler 中硬編碼方法名。Proxy.newProxyInstance(loader, interfaces, handler):動態生成實作指定介面的代理類,實例化並返回。代理物件 IS-A 目標介面,可直接轉型呼叫。target.studyAtHome() 會繞過代理,無法執行增強邏輯。Proxy 將目標與 Handler 「編織」在一起。代理:準備買菜 代理:準備洗菜 代理:準備炒菜 代理:準備煮飯 ----------------- 小明在 SCHOOL 學習 ----------------- 代理:購買補品 代理:熬製補品 代理:加入佐料 代理:準備買菜 代理:準備洗菜 代理:準備炒菜 代理:準備煮飯 ----------------- 小明在 HOME 學習 ----------------- 代理:購買補品 代理:熬製補品 代理:加入佐料
| 特性 | JDK Proxy | CGLIB |
|---|---|---|
| 機制 | 介面代理(生成實作介面的類) | 繼承代理(生成目標類子類) |
| 目標要求 | 必須實作至少一個介面 | 無需介面,但類/方法不可為 final |
| 效能 | 生成快,呼叫稍慢(反射) | 生成慢(字節碼操作),呼叫快(FastClass) |
| Spring 預設 | 有介面用 JDK Proxy | 無介面或強制 proxy-target-class=true 用 CGLIB |
Spring AOP 底層即封裝此機制,@Aspect 切面對應 InvocationHandler,@Pointcut 對應方法攔截判斷,實現聲明式 AOP。