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

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

     編輯:Java 相關知識

代理模式核心角色

  • Subject:共同介面
  • RealSubject:真實物件(被代理者)
  • Proxy:代理物件,控制對真實物件存取,可添加增強邏輯

一、靜態代理(編譯期確定代理類)

1. 基於介面實作

// 介面
public interface Hello { void say(String msg); }

// 真實物件
public class HelloImpl implements Hello {
    public void say(String msg) { System.out.println("Hi, " + msg); }
}

// 代理物件(實作相同介面)
public class HelloProxy implements Hello {
    private Hello target;
    public HelloProxy(Hello target) { this.target = target; }
    public void say(String msg) {
        before();
        target.say(msg);
        after();
    }
    private void before() { System.out.println("Before"); }
    private void after() { System.out.println("After"); }
}

2. 基於繼承實作

public class HelloTarget {
    public void sayHello(String name) { System.out.println("Hi, " + name); }
}

public class HelloProxy extends HelloTarget {
    private HelloTarget target;
    public HelloProxy(HelloTarget target) { this.target = target; }
    @Override
    public void sayHello(String name) {
        before();
        target.sayHello(name);
        after();
    }
    // before/after 同前
}

靜態代理缺點

  • 每個目標類需手寫代理類,代碼冗餘
  • 介面新增方法,目標類與代理類都要改

二、JDK 動態代理(執行期生成代理類,基於介面)

HelloImpl target = new HelloImpl();
Hello proxy = (Hello) Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before");
            Object ret = method.invoke(target, args); // 反射調用目標方法
            System.out.println("after");
            return ret;
        }
    }
);
proxy.say("LZHL");

核心類

  • Proxy.newProxyInstance(loader, interfaces, handler)
  • InvocationHandler.invoke(proxy, method, args):攔截所有方法調用

限制

目標類必須實作至少一個介面,否則無法使用 JDK 動態代理。

三、CGLIB 動態代理(執行期生成子類,基於繼承)

需引入依賴:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.4</version>
</dependency>
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Hi.class); // 目標類無需實作介面
enhancer.setCallback(new MethodInterceptor() {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("before");
        Object ret = proxy.invokeSuper(obj, args); // 調用父類方法
        System.out.println("after");
        return ret;
    }
});
Hi proxy = (Hi) enhancer.create();
proxy.sayHi("LXY");

核心差異

項目JDK 動態代理CGLIB 動態代理
機制實作介面繼承目標類
目標類要求必須實作介面無要求(非 final 類)
核心類Proxy + InvocationHandlerEnhancer + MethodInterceptor
調用方式method.invoke(target, args)proxy.invokeSuper(obj, args)
final 方法可代理無法代理

選型建議

  • 目標類有介面 → **優先 JDK**(JDK 內建、效能穩定)
  • 目標類無介面、或需代理非介面方法 → **CGLIB**
  • Spring AOP 預設:有介面用 JDK,無介面用 CGLIB(可強制 proxy-target-class=true 全用 CGLIB)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved