首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Spring AOP 動態代理實作:JDK Proxy 與 InvocationHandler

     編輯:Java 綜合教學

Spring AOP 動態代理實作:JDK Proxy 與 InvocationHandler

動態代理解決靜態代理的程式碼冗餘問題:代理類在執行期動態生成,單一 InvocationHandler 統一處理所有方法攔截,無需為每個目標類寫專屬代理類。

一、動態代理核心組件

  • java.lang.reflect.Proxy:提供 newProxyInstance() 產生代理物件。
  • java.lang.reflect.InvocationHandler:介面,唯一方法 invoke(Object proxy, Method method, Object[] args) 定義攔截邏輯。
  • 目標類需實作一個或多個介面(JDK Proxy 限制)。

二、完整程式碼範例:同樣的高三學生場景

1. 共同介面

package aop_002;

public interface G3Student {
    void studyAtHome(String core);
    void studyAtSchool(String core);
}

2. 目標類:高三男生

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 學習");
    }
}

3. 通用處理器:家長代理邏輯

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();
    }
}

4. 測試類:建立動態代理

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("小明");
    }
}

三、關鍵點解析

  1. Handler 持有 Object target:不綁定具體介面,同一 Handler 可代理 G3Student_boy、G3Student_girl 等任意實作 G3Student 的類別。
  2. method.invoke(target, args):反射呼叫目標方法,method 由 JVM 自動傳入(被呼叫的方法),args 為實際參數。無需在 Handler 中硬編碼方法名。
  3. Proxy.newProxyInstance(loader, interfaces, handler):動態生成實作指定介面的代理類,實例化並返回。代理物件 IS-A 目標介面,可直接轉型呼叫。
  4. 為何不直接用 target 呼叫? 直接呼叫 target.studyAtHome() 會繞過代理,無法執行增強邏輯。Proxy 將目標與 Handler 「編織」在一起。

四、輸出結果(與靜態代理完全一致)

代理:準備買菜
代理:準備洗菜
代理:準備炒菜
代理:準備煮飯
-----------------
小明在 SCHOOL 學習
-----------------
代理:購買補品
代理:熬製補品
代理:加入佐料

代理:準備買菜
代理:準備洗菜
代理:準備炒菜
代理:準備煮飯
-----------------
小明在 HOME 學習
-----------------
代理:購買補品
代理:熬製補品
代理:加入佐料

五、JDK Proxy vs CGLIB

特性JDK ProxyCGLIB
機制介面代理(生成實作介面的類)繼承代理(生成目標類子類)
目標要求必須實作至少一個介面無需介面,但類/方法不可為 final
效能生成快,呼叫稍慢(反射)生成慢(字節碼操作),呼叫快(FastClass)
Spring 預設有介面用 JDK Proxy無介面或強制 proxy-target-class=true 用 CGLIB

Spring AOP 底層即封裝此機制,@Aspect 切面對應 InvocationHandler,@Pointcut 對應方法攔截判斷,實現聲明式 AOP。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved