1. 程式人生 > 其它 >Java 動態代理 2022-3-17

Java 動態代理 2022-3-17

代理模式:一種結構型設計模式;指的是給某一個物件提供一個代理,並由代理物件來控制對真實物件的訪問

分類:根據位元組碼的建立時機來分類,可以分為 靜態代理 和 動態代理
靜態代理:在程式執行前就已經存在代理類的位元組碼檔案,代理類 和 真正實現業務邏輯的類 的關係在執行前就確定了
動態代理:位元組碼是在程式執行期間由JVM根據反射等機制動態的生成,所以在執行前並不存在代理類的位元組碼檔案

雖然靜態代理實現簡單,且不侵入原始碼,但是,當場景稍微複雜一些的時候,靜態代理的缺點也會暴露出來

靜態代理的缺點
1、 當需要代理多個類的時候,由於代理物件要實現與目標物件一致的介面,有兩種方式:

  • 只維護一個代理類,由這個代理類實現多個介面,但是這樣就導致代理類過於龐大
  • 新建多個代理類,每個目標物件對應一個代理類,但是這樣會產生過多的代理類
    2、 當介面需要增加、刪除、修改方法的時候,目標物件與代理類都要同時修改,不易維護。



CGLIB 建立動態代理類的模式是:

1、查詢目標類上的所有非final 的public型別的方法定義;
2、將這些方法的定義轉換成位元組碼;
3、將組成的位元組碼轉換成相應的代理的class物件;
4、實現 MethodInterceptor介面,用來處理對代理類上所有方法的請求

面試題
來源於網上,用於幫助理解和掌握,歡迎補充

描述動態代理的幾種實現方式?分別說出相應的優缺點
代理可以分為 "靜態代理" 和 "動態代理",動態代理又分為 "JDK動態代理" 和 "CGLIB動態代理" 實現。

靜態代理:代理物件和實際物件都繼承了同一個介面,在代理物件中指向的是實際物件的例項,這樣對外暴露的是代理物件而真正呼叫的是 Real Object

優點:可以很好的保護實際物件的業務邏輯對外暴露,從而提高安全性
缺點:不同的介面要有不同的代理類實現,會很冗餘

JDK 動態代理:
為了解決靜態代理中,生成大量的代理類造成的冗餘;

JDK 動態代理只需要實現 InvocationHandler 介面,重寫 invoke 方法便可以完成代理的實現,

jdk的代理是利用反射生成代理類 Proxyxx.class 代理類位元組碼,並生成物件

jdk動態代理之所以只能代理介面是因為代理類本身已經extends了Proxy,而java是不允許多重繼承的,但是允許實現多個介面

優點:解決了靜態代理中冗餘的代理實現類問題

缺點:JDK 動態代理是基於介面設計實現的,如果沒有介面,會拋異常。

CGLIB 代理:

由於 JDK 動態代理限制了只能基於介面設計,而對於沒有介面的情況,JDK方式解決不了;

CGLib 採用了非常底層的位元組碼技術,其原理是通過位元組碼技術為一個類建立子類,並在子類中採用方法攔截的技術攔截所有父類方法的呼叫,順勢織入橫切邏輯,來完成動態代理的實現。

實現方式實現 MethodInterceptor 介面,重寫 intercept 方法,通過 Enhancer 類的回撥方法來實現。

但是CGLib在建立代理物件時所花費的時間卻比JDK多得多,所以對於單例的物件,因為無需頻繁建立物件,用CGLib合適,反之,使用JDK方式要更為合適一些。

同時,由於CGLib由於是採用動態建立子類的方法,對於final方法,無法進行代理。

優點:沒有介面也能實現動態代理,而且採用位元組碼增強技術,效能也不錯。

缺點:技術實現相對難理解些。

CGlib 對介面實現代理?
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import proxy.UserService;
import java.lang.reflect.Method;

/**

  • 建立代理類的工廠 該類要實現 MethodInterceptor 介面。

  • 該類中完成三樣工作:

  • (1)宣告目標類的成員變數,並建立以目標類物件為引數的構造器。用於接收目標物件

  • (2)定義代理的生成方法,用於建立代理物件。方法名是任意的。代理物件即目標類的子類

  • (3)定義回撥介面方法。對目標類的增強這在這裡完成
    */
    public class CGLibFactory implements MethodInterceptor {
    // 宣告目標類的成員變數
    private UserService target;

    public CGLibFactory(UserService target) {
    this.target = target;
    }
    // 定義代理的生成方法,用於建立代理物件
    public UserService myCGLibCreator() {
    Enhancer enhancer = new Enhancer();
    // 為代理物件設定父類,即指定目標類
    enhancer.setSuperclass(UserService.class);
    /**
    * 設定回撥介面物件 注意,只所以在setCallback()方法中可以寫上this,
    * 是因為MethodIntecepter介面繼承自Callback,是其子介面
    */
    enhancer.setCallback(this);
    return (UserService) enhancer.create();// create用以生成CGLib代理物件
    }
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    System.out.println("start invoke " + method.getName());
    Object result = method.invoke(target, args);
    System.out.println("end invoke " + method.getName());
    return result;
    }
    }

學習地址:
https://www.cnblogs.com/whirly/p/10154887.html