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

Java 代理模式完整實戰:靜態代理、動態代理與 CGLIB 代理深度對比

     編輯:Java 綜合教學

Java 代理模式完整實戰:靜態代理、動態代理與 CGLIB 代理深度對比

代理模式為其他物件提供代理以控制存取,是 Spring AOP 的核心基礎。本文完整實作三種代理方式,深度對比原理、優缺點與適用場景。

共同介面定義

public interface IUserDao {
    void save();
}

一、靜態代理

特點:代理類別與目標類別實作同一介面,編譯期確定關係。

// 目標物件
public class UserDao implements IUserDao {
    public void save() {
        System.out.println("-----已經保存數據!!!------");
    }
}

// 代理物件
public class UserDaoProxy implements IUserDao {
    private IUserDao target;
    public UserDaoProxy(IUserDao target) { this.target = target; }
    
    public void save() {
        System.out.println("開始事務...");
        target.save();           // 執行目標方法
        System.out.println("提交事務...");
    }
}

// 測試
public class App {
    public static void main(String[] args) {
        IUserDao target = new UserDao();
        IUserDao proxy = new UserDaoProxy(target);
        proxy.save();
    }
}

缺點:每個目標類別需手寫代理類別,程式碼冗餘、維護成本高。

二、JDK 動態代理

核心:java.lang.reflect.Proxy + InvocationHandler,執行期動態生成代理類別。

public class ProxyFactory {
    private Object target;
    public ProxyFactory(Object target) { this.target = target; }
    
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new InvocationHandler() {
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("開啟事務");
                    Object returnValue = method.invoke(target, args); // 反射呼叫目標
                    System.out.println("提交事務");
                    return returnValue;
                }
            }
        );
    }
}

// 測試
public class App {
    public static void main(String[] args) {
        IUserDao target = new UserDao();
        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
        System.out.println(proxy.getClass()); // class com.sun.proxy.$Proxy0
        proxy.save();
    }
}

限制:目標類別必須實作介面,代理只能代理介面方法。

三、CGLIB 代理

核心:繼承目標類別,覆寫非 final 方法,無需介面。需引入 cglib 或 spring-core 依賴。

public class ProxyFactory implements MethodInterceptor {
    private Object target;
    public ProxyFactory(Object target) { this.target = target; }
    
    public Object getProxyInstance() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass()); // 設定父類別
        enhancer.setCallback(this);                // 設定攔截器
        return enhancer.create();                  // 建立子類別代理
    }
    
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("開始事務.....");
        Object returnValue = method.invoke(target, args);
        System.out.println("提交事務.....");
        return returnValue;
    }
}

// 測試(目標類別無需實作介面)
public class App {
    public static void main(String[] args) {
        UserDao target = new UserDao(); // 普通類別
        UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
        System.out.println(proxy.getClass()); // class UserDao$$EnhancerByCGLIB$$xxx
        proxy.save();
    }
}

三種代理深度對比

維度靜態代理JDK 動態代理CGLIB 代理
實作方式手寫代理類別Proxy.newProxyInstanceEnhancer 建立子類別
目標類別要求實作介面必須實作介面無需介面(普通類別)
代理型別實作同一介面實作同一介面繼承目標類別
方法攔截範圍介面所有方法介面所有方法非 final/public 方法
效能特徵直接呼叫,最快反射呼叫,較慢FastClass 機制,較快
適用場景固定、少量代理介面導向設計無介面類別、高效能需求

Spring AOP 選擇策略

  • 目標實作介面 → 預設使用 JDK 動態代理
  • 目標未實作介面 / 強制使用 CGLIB → proxy-target-class="true"
  • Spring Boot 2.0+ 預設 proxy-target-class=true(偏好 CGLIB)

總結

代理模式是「關注點分離」的經典實踐:將横切關注點(事務、日誌、安全)從業務邏輯中抽離。理解三種代理的底層機制(介面實作 vs 繼承、反射 vs FastClass),能在架構設計與問題排查中做出正確選擇。實務中,JDK 動態代理與CGLIB是主流,靜態代理僅用於簡單固定場景。

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