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

Java 代理模式全景解析:靜態代理、JDK 動態代理、CGLIB 動態代理原理與實戰

     編輯:Java 綜合教學

Java 代理模式全景解析:靜態代理、JDK 動態代理、CGLIB 動態代理原理與實戰

代理模式為目標對象提供替身,控制對其訪問,核心角色:Subject(抽象接口)、RealSubject(真實對象)、Proxy(代理類)。本文系統對比三種實現方式,深入源碼級原理,附完整可運行代碼。

一、靜態代理:手寫代理類

代碼結構

// 1. 抽象接口
public interface IAnimal { void bark(); }

// 2. 真實對象
public class Dog implements IAnimal {
    private String name;
    public Dog(String name) { this.name = name; }
    public void bark() { System.out.println(name + " bark: wang wang..."); }
}

// 3. 代理類
public class DogProxy implements IAnimal {
    private Dog dog;
    public DogProxy(Dog dog) { this.dog = dog; }
    public void bark() {
        long t = System.currentTimeMillis();
        System.out.println("dog will bark...");
        dog.bark();
        System.out.println("dog barked, cost " + (System.currentTimeMillis()-t) + "ms");
    }
}

// 4. 使用
IAnimal dog = new Dog("大黃");
IAnimal proxy = new DogProxy(dog);
proxy.bark();

優缺點

  • ✅ 業務邏輯與橫切關注點分離
  • ❌ 一個目標類需一個代理類,維護成本高,無法復用

二、JDK 動態代理:運行時生成實現接口的代理類

核心組件

  • java.lang.reflect.Proxy:生成代理類的工廠
  • java.lang.reflect.InvocationHandler:攔截方法調用的核心接口

實戰代碼

// 1. 實現 InvocationHandler
public class DogProxyHandler implements InvocationHandler {
    private Object target;
    public DogProxyHandler(Object target) { this.target = target; }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before: " + method.getName());
        Object result = method.invoke(target, args); // 反射調用目標方法
        System.out.println("after: " + method.getName());
        return result;
    }
}

// 2. 生成代理並調用
IAnimal dog = new Dog("大黃");
InvocationHandler handler = new DogProxyHandler(dog);
IAnimal proxy = (IAnimal) Proxy.newProxyInstance(
    dog.getClass().getClassLoader(),
    dog.getClass().getInterfaces(),
    handler
);
proxy.bark();

輸出

proxy class: class com.sun.proxy.$Proxy0
before: bark
大黃 bark: wang wang...
after: bark

源碼級原理:生成的 $Proxy0 類結構

public final class $Proxy0 extends Proxy implements IAnimal {
    protected $Proxy0(InvocationHandler h) { super(h); }
    public final void bark() {
        super.h.invoke(this, m3, null); // m3 = IAnimal.bark Method 對象
    }
    // equals/hashCode/toString 同理委派給 InvocationHandler
    static {
        m3 = Class.forName("...IAnimal").getMethod("bark");
    }
}

關鍵點:

  • 代理類繼承 Proxy、實現目標所有接口
  • 每個方法調用轉發給 InvocationHandler.invoke()
  • 靜態代碼塊緩存 Method 對象提升性能
  • 限制:目標類必須實現至少一個接口

三、CGLIB 動態代理:繼承目標類生成子類(無需接口)

Maven 依賴

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.4</version>
</dependency>

核心組件

  • net.sf.cglib.proxy.Enhancer:字節碼增強器,生成子類
  • MethodInterceptor:方法攔截器(對標 InvocationHandler)
  • CallbackFilter:按方法名決定使用哪個 Callback

實戰:按方法名選擇是否增強

// 目標類(無接口)
public class Animal {
    public void bark() { System.out.println("i am 大黃!"); }
    public String singSong(String name) { return name + " is singing!"; }
    public String testt(String name) { return name + " is testing!"; }
}

// 1. 通用攔截器:前後加日志
public class AnimalWrapper implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("BEFORE");
        Object result = proxy.invokeSuper(obj, args); // 調用父類方法
        System.out.println("AFTER");
        return result;
    }
}

// 2. 過濾器:方法名長度為 5 的才增強 (bark=4, singSong=8, testt=5)
public class ApiFilter implements CallbackFilter {
    @Override public int accept(Method method) {
        return method.getName().length() == 5 ? 1 : 0; // 1 用 Callback[1]=NoOp
    }
}

// 3. 組裝與測試
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Animal.class);
enhancer.setCallbacks(new Callback[]{new AnimalWrapper(), NoOp.INSTANCE}); // 0:增強, 1:無操作
enhancer.setCallbackFilter(new ApiFilter());
Animal proxy = (Animal) enhancer.create();

proxy.bark();       // BEFORE ... AFTER
proxy.singSong("老劉"); // BEFORE ... AFTER
proxy.testt("大黃");   // 直接輸出:大黃 is testing! (無增強)

生成類結構 (反編譯)

public class Animal$$EnhancerByCGLIB$$223151cf extends Animal implements Factory {
    private MethodInterceptor CGLIB$CALLBACK_0;
    private NoOp CGLIB$CALLBACK_1;
    private static CallbackFilter CGLIB$CALLBACK_FILTER;

    public final void bark() {
        CGLIB$CALLBACK_0.intercept(this, CGLIB$bark$0$Method, CGLIB$emptyArgs, CGLIB$bark$0$Proxy);
    }
    public final String testt(String s) {
        CGLIB$CALLBACK_1.intercept(...); // NoOp 直接調用父類
    }
    // FastClass 機制:索引式方法調用,比反射快
}

CGLIB 核心優勢

  • 無需接口,通過繼承實現代理
  • FastClass 機制:為目標類與代理類生成索引類,方法調用轉為 switch(index) 直接分發,避免反射開銷
  • 支持細粒度方法級過濾 (CallbackFilter)

四、三種代理對比總結

維度靜態代理JDK 動態代理CGLIB 動態代理
目標要求任意類必須實現接口非 final 類、非 final 方法
生成方式編譯期手寫運行時生成實現接口的類運行時生成子類
性能最優 (直接調用)反射調用,JDK 8+ 優化明顯FastClass 索引調用,通常最快
適用場景固定、少量目標接口驅動、框架級 AOP (Spring AOP 默認)無接口類、第三方庫、高性能場景
Spring 選擇策略-目標實現接口 → JDK目標無接口或強制 proxy-target-class=true → CGLIB

五、最佳實踐建議

  1. 優先定義接口,使用 JDK 動態代理(標準、JDK 內置、無第三方依賴)
  2. 無法修改第三方類或無接口時,引入 CGLIB
  3. Spring Boot 2.x 默認 spring.aop.proxy-target-class=true(全 CGLIB),可配置為 false 使用 JDK
  4. 避免對 final 方法/類代理(CGLIB 無法繼承),改用組合模式
  5. 代理邏輯保持輕量,避免在 invoke/intercept 中執行耗時操作
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved