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

Java 實現動態代理

     編輯:Java 相關知識

JDK 動態代理:運行時生成代理類,攔截方法調用

Java 提供 java.lang.reflect.Proxy 和 InvocationHandler 實現動態代理。代理類在運行時生成,實現指定接口,將方法調用分發給 InvocationHandler.invoke(),實現 AOP、遠程調用、事務管理等橫切關注點分離。

核心組件

  • InvocationHandler:接口,定義 invoke(Object proxy, Method method, Object[] args),處理代理方法調用。
  • Proxy:工具類,提供 newProxyInstance(ClassLoader, Class[] interfaces, InvocationHandler) 創建代理實例。

完整範例:日誌攔截器

// 1. 定義業務接口
public interface Subject {
    void request();
}

// 2. 真實主題
public class RealSubject implements Subject {
    @Override public void request() { System.out.println("真實業務邏輯執行"); }
}

// 3. 代理處理器:攔截並增強
public class LoggingHandler implements InvocationHandler {
    private final Object target; // 被代理對象
    public LoggingHandler(Object target) { this.target = target; }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("【前置】調用方法: " + method.getName());
        long start = System.currentTimeMillis();
        Object result = method.invoke(target, args); // 反射調用真實方法
        System.out.println("【後置】耗時: " + (System.currentTimeMillis() - start) + "ms");
        return result;
    }
}

// 4. 客戶端
public class Client {
    public static void main(String[] args) {
        RealSubject real = new RealSubject();
        InvocationHandler handler = new LoggingHandler(real);
        // 生成代理實例
        Subject proxy = (Subject) Proxy.newProxyInstance(
            real.getClass().getClassLoader(),
            real.getClass().getInterfaces(),
            handler
        );
        proxy.request(); // 觸發攔截
    }
}

進階範例:通用代理工廠

public class ProxyFactory {
    @SuppressWarnings("unchecked")
    public static  T createProxy(T target, InvocationHandler handler) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            handler
        );
    }
}

// 使用
Subject proxy = ProxyFactory.createProxy(new RealSubject(), new LoggingHandler(real));

Vector 代理範例:方法調用日誌

public class VectorProxy implements InvocationHandler {
    private final Object target;
    private VectorProxy(Object target) { this.target = target; }
    public static List newInstance() {
        return (List) Proxy.newProxyInstance(
            Vector.class.getClassLoader(),
            new Class[]{List.class},
            new VectorProxy(new Vector<>())
        );
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("呼叫: " + method.getName() + " 參數: " + Arrays.toString(args));
        return method.invoke(target, args);
    }
}

// 測試
List list = VectorProxy.newInstance();
list.add("A");
list.add("B");
System.out.println(list); // 觸發 toString() 也會被攔截

關鍵限制與注意點

  • 只能代理接口:JDK 動態代理要求目標類實現至少一個接口。代理類繼承 Proxy,實現目標接口。無法代理普通類(需用 CGLIB/ByteBuddy 繼承子類代理)。
  • final 方法不會被攔截:Proxy 生成的代理類中 final 方法直接調用父類實現,不走 invoke。
  • 性能開銷:反射調用比直接調用慢,高頻熱點路徑需考慮。
  • 序列化:動態代理類實現 Serializable,但序列化後反序列化需相同 ClassLoader 環境。

應用場景

Spring AOP、RPC 框架(Dubbo、gRPC Java)、MyBatis Mapper 動態實現、事務管理、權限檢查、緩存、日誌監控等。

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