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

Java 代理模式深度解析:靜態代理、JDK 動態代理與 CGLIB 動態代理完整實現

     編輯:Java 綜合教學

前言

代理模式是結構型設計模式的核心,通過代理對象控制對真實對象的訪問,可在不修改原類碼前提下增強功能(日誌、事務、權限、緩存等)。Spring AOP 底層即基於動態代理實現。本文從概念到代碼,徹底講透三種代理實現方式。

一、核心角色與概念

角色說明對應代碼
Subject (主題接口)定義代理與真實對象的公共方法Factory 接口
RealSubject (真實主題)實際業務邏輯實現者NikeFactory
Proxy (代理類)持有真實對象引用,控制訪問並可添加橫切邏輯NikeFactoryProxy / 動態代理類
Client (客戶端)通過代理調用業務方法Main / Test 類

二、靜態代理

特點:代理類在編譯期就確定,一個真實類對應一個代理類,代碼冗餘高。

1. 接口定義

public interface Factory {
    void say();
    void produce();
}

2. 真實主題

public class NikeFactory implements Factory {
    @Override
    public void say() {
        System.out.println("我要開始生產了!");
    }

    @Override
    public void produce() {
        System.out.println("正在生產 Nike 衣服!");
    }
}

3. 代理類(手動編寫)

public class NikeFactoryProxy implements Factory {
    private final NikeFactory nikeFactory = new NikeFactory();

    @Override
    public void say() {
        before();
        nikeFactory.say();
        after();
    }

    @Override
    public void produce() {
        before();
        nikeFactory.produce();
        after();
    }

    private void before() {
        System.out.println("在正式開始生產之前,我要說兩句!");
    }

    private void after() {
        System.out.println("大家做的不錯!");
    }
}

4. 客戶端測試

@Test
public void testStaticProxy() {
    Factory proxy = new NikeFactoryProxy();
    proxy.say();
    proxy.produce();
}
/* 輸出:
在正式開始生產之前,我要說兩句!
我要開始生產了!
大家做的不錯!
在正式開始生產之前,我要說兩句!
正在生產 Nike 衣服!
大家做的不錯!
*/

三、JDK 動態代理

核心機制:運行時通過 java.lang.reflect.Proxy 生成實現目標接口的匿名類,**前提:目標類必須實現一個或多個接口**。

1. 基礎用法(匿名內部類)

@Test
public void testJDKProxy() {
    Factory proxy = (Factory) Proxy.newProxyInstance(
        NikeFactory.class.getClassLoader(),
        NikeFactory.class.getInterfaces(),
        new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("【JDK動態代理】before: " + method.getName());
                Object result = method.invoke(new NikeFactory(), args);
                System.out.println("【JDK動態代理】after: " + method.getName());
                return result;
            }
        }
    );
    proxy.say();
    proxy.produce();
}

2. 通用代理工廠(復用性強)

public class DynamicProxy implements InvocationHandler {
    private final Object target;

    public DynamicProxy(Object target) {
        this.target = target;
    }

    @SuppressWarnings("unchecked")
    public  T getProxy() {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            this
        );
    }

    @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;
    }
}

// 使用
@Test
public void testJDKProxy2() {
    Factory proxy = new DynamicProxy(new NikeFactory()).getProxy();
    proxy.say();
    proxy.produce();
}

四、CGLIB 動態代理

核心機制:通過繼承目標類、重寫非 final 方法實現代理,**無需目標類實現接口**;底層使用 ASM 字節碼操作框架。需額外引入依賴:

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

實現代碼

public class CglibProxy implements MethodInterceptor {
    private static final CglibProxy INSTANCE = new CglibProxy();

    private CglibProxy() {}

    public static CglibProxy getInstance() {
        return INSTANCE;
    }

    @SuppressWarnings("unchecked")
    public  T getProxy(Class cls) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(cls);
        enhancer.setCallback(this);
        return (T) enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("【CGLIB】before: " + method.getName());
        Object result = proxy.invokeSuper(obj, args); // 調用父類方法
        System.out.println("【CGLIB】after: " + method.getName());
        return result;
    }
}

// 使用
@Test
public void testCglibProxy2() {
    NikeFactory proxy = CglibProxy.getInstance().getProxy(NikeFactory.class);
    proxy.say();
    proxy.produce();
}

五、三種方式對比與選型指南

維度靜態代理JDK 動態代理CGLIB 動態代理
目標類要求無必須實現接口無(但不能代理 final 類/方法)
生成時機編譯期運行時運行時
代理類關係實現同一接口實現同一接口繼承目標類
性能(調用)最快(直接調用)反射調用,稍慢FastClass 機制,較 JDK 快
適用場景簡單、固定增強接口驅動、框架級 AOP無接口類、第三方庫增強

六、Spring AOP 的選擇策略

  • 目標類實現接口 → 默認使用 JDK 動態代理。
  • 目標類未實現接口 / 強制使用 CGLIB → @EnableAspectJAutoProxy(proxyTargetClass = true) 或 aop:config proxy-target-class="true"。
  • Spring Boot 2.0+ 默認 proxyTargetClass = true,優先 CGLIB。

七、總結

  • 理解代理模式本質:攔截 → 增強 → 委派。
  • 靜態代理適合教學與極簡場景;生產中幾乎不直接使用。
  • JDK 動態代理依賴接口,純 Java 實現,無第三方依賴。
  • CGLIB 通過繼承實現,功能更強(可代理無接口類),但需注意 final 限制。
  • 掌握 InvocationHandler 與 MethodInterceptor 兩大核心攔截接口,即掌握了 AOP 的靈魂。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved