Java基礎-動態代理

一、代理模式簡介

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

根據代理類的創建時機和創建方式的不同,可以將其分爲靜態代理和動態代理兩種形式:

在程序運行前即編譯時就存在的代理類是爲靜態代理
在程序運行時根據需要動態創建代理類及其實例爲動態代理

靜態代理,必需手動創建N個代理類,這顯然讓人相當不爽。

動態代理則可以簡單地爲各個主題類分別生成代理類,共享“預處理,後處理”功能,這樣可以大大減小程序規模,這也是動態代理的一大亮點。

在動態代理中,代理類是在運行時期生成的。因此,相比靜態代理,動態代理可以很方便地對委託類的相關方法進行統一增強處理,如添加方法調用次數、添加日誌功能等等

 

二、JDK動態代理機制的相關類與接口

java.lang.reflect.Proxy:該類用於動態生成代理類,只需傳入被監控對象隸屬的類文件在內存中真實地址、被監控對象隸屬的類文件實現接口以及InvocationHandler通知對象便可爲目標接口生成代理類及代理對象。

// 方法 1: 該方法用於獲取指定代理對象所關聯的

InvocationHandler static InvocationHandler getInvocationHandler(Object proxy)

// 方法 2:該方法用於獲取關聯於指定類裝載器和一組接口的動態代理類的類對象

static Class getProxyClass(ClassLoader loader, Class[] interfaces)

// 方法 3:該方法用於判斷指定類是否是一個動態代理類

static boolean isProxyClass(Class cl)

// 方法 4:該方法用於爲指定類裝載器、一組接口及調用處理器生成動態代理類實例

static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h)

java.lang.reflect.InvocationHandler:該接口包含一個invoke方法,通過該方法實現對委託類的代理的訪問,是代理類完整邏輯的集中體現,包括要切入的增強邏輯和進行反射執行的真實業務邏輯。

Object invoke(Object proxy, Method method, Object[] args)

該方法是代理類完整邏輯的集中體現。在被監控行爲將要執行時,會被JVM攔截。被監控行爲和行爲實現方法會被作爲參數輸送invoke,通常通過反射完成對具體角色業務邏輯的調用,並對其進行增強。
第一個參數既是代理類實例。
第二個參數是被調用的方法對象。
第三個方法是調用參數。

java.lang.ClassLoader:類加載器類,負責將類的字節碼裝載到Java虛擬機中併爲其定義類對象,然後該類才能被使用。Proxy靜態方法生成動態代理類同樣需要通過類加載器來進行加載才能使用,它與普通類的唯一區別就是其字節碼是由JVM在運行時動態生成的而非預存在於任何一個.class 文件中。

 

代碼實例:

public void proxyForPeople(){
    User user = new User();
    People userProxy = (People) Proxy.newProxyInstance(user.getClass().getClassLoader(),
            user.getClass().getInterfaces(), (proxy, methods, args) -> {
                if(methods.getName().equals("toExcute")){
                    System.out.println("preparing......");
                    return methods.invoke(user, args);
                }
                if(methods.getName().equals("getStatus")){
                    System.out.println("status types [coding, rest, learning]");
                    return methods.invoke(user, args);
                }
                return null;
            });

    userProxy.toExcute();
    userProxy.getStatus();
}

 

三、動態代理的原理

          其實大概就是把接口複製出來,通過這些接口和類加載器,拿到這個代理類cl。然後通過反射的技術複製拿到代理類的構造函數(這部分代碼在Class類中的getConstructor0方法),最後通過這個構造函數new個一對象出來,同時用InvocationHandler綁定這個對象。

 

四、實現動態代理的方式

了讓生成的代理類與目標對象(真實主題角色)保持一致性,從現在開始將介紹以下兩種最常見的方式:

    通過實現接口的方式 -> JDK動態代理

    通過繼承類的方式 -> CGLIB動態代理

JDK代理需要一組需要實現的接口,然後通過這些接口獲取構造方法,用這個構造方法和InvocationHandler,實例化一個對象出來。所以JDK的方式是基於接口的。

而CGLIB的代理是基於類的,用目標類生成一個子類,子類重寫父類的方法,從而達到動態代理的效果。

一般情況下使用jdk動態代理,如果目標對象的代理至少實現了一個接口,那麼就用JDK動態代理,所有由目標對象實現的接口將全部都被代理。如果目標對象沒有實現任何接口,那麼就用CGLIB代理。

 

五、動態代理的好處

    比較靈活,可以在運行的時候才切入改變類的方法,而不需要預先定義它。

動態代理一般我們比較少去手寫,但我們用得其實非常多。在Spring項目中用的註解,例如依賴注入的@Bean、@Autowired,事務註解@Transactional等都有用到,換言之就是Srping的AOP(切面編程)。

這種場景的使用是動態代理最佳的落地點,可以非常靈活地在某個類,某個方法,某個代碼點上切入我們想要的內容,就是動態代理其中的內容。

 

 

參考:

https://www.jianshu.com/p/95970b089360

https://www.jianshu.com/p/4dcc74b63f1c

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章