Java JDK 動態代理使用及實現原理分析

尋找"四葉草"發表於2016-08-08

一、什麼是代理?

代理是一種常用的設計模式,其目的就是為其他物件提供一個代理以控制對某個物件的訪問。代理類負責為委託類預處理訊息,過濾訊息並轉發訊息,以及進行訊息被委託類執行後的後續處理。

代理模式UML圖:

為了保持行為的一致性,代理類和委託類通常會實現相同的介面,所以在訪問者看來兩者沒有絲毫的區別。通過代理類這中間一層,能有效控制對委託類物件的直接訪問,也可以很好地隱藏和保護委託類物件,同時也為實施不同控制策略預留了空間,從而在設計上獲得了更大的靈活性。Java 動態代理機制以巧妙的方式近乎完美地實踐了代理模式的設計理念。

二、Java 動態代理類

Java動態代理類位於java.lang.reflect包下,一般主要涉及到以下兩個類:

(1)Interface InvocationHandler:該介面中僅定義了一個方法

[java] view plain copy

  1. publicobject invoke(Object obj,Method method, Object[] args)

在實際使用時,第一個引數obj一般是指代理類,method是被代理的方法,如上例中的request(),args為該方法的引數陣列。這個抽象方法在代理類中動態實現。

(2)Proxy:該類即為動態代理類,其中主要包含以下內容:

protected Proxy(InvocationHandler h):建構函式,用於給內部的h賦值。

static Class getProxyClass (ClassLoaderloader, Class[] interfaces):獲得一個代理類,其中loader是類裝載器,interfaces是真實類所擁有的全部介面的陣列。

static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h):返回代理類的一個例項,返回後的代理類可以當作被代理類使用(可使用被代理類的在Subject介面中宣告過的方法)

所謂DynamicProxy是這樣一種class:它是在執行時生成的class,在生成它時你必須提供一組interface給它,然後該class就宣稱它實現了這些interface。你當然可以把該class的例項當作這些interface中的任何一個來用。當然,這個DynamicProxy其實就是一個Proxy,它不會替你作實質性的工作,在生成它的例項時你必須提供一個handler,由它接管實際的工作。

在使用動態代理類時,我們必須實現InvocationHandler介面

通過這種方式,被代理的物件(RealSubject)可以在執行時動態改變,需要控制的介面(Subject介面)可以在執行時改變,控制的方式(DynamicSubject類)也可以動態改變,從而實現了非常靈活的動態代理關係。

動態代理步驟
1.建立一個實現介面InvocationHandler的類,它必須實現invoke方法
2.建立被代理的類以及介面
3.通過Proxy的靜態方法
newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)建立一個代理
4.通過代理呼叫方法

三、JDK的動態代理怎麼使用?

1、需要動態代理的介面:

2、需要代理的實際物件

3、呼叫處理器實現類

4、測試

5、輸出結果如下:

演示demo下載地址:http://download.csdn.net/detail/xunzaosiyecao/9597388

四、動態代理怎麼實現的?

從使用程式碼中可以看出,關鍵點在:

通過跟蹤提示程式碼可以看出:當代理物件呼叫真實物件的方法時,其會自動的跳轉到代理物件關聯的handler物件的invoke方法來進行呼叫。
也就是說,當程式碼執行到:
subject.SayHello(“jiankunking”)這句話時,會自動呼叫InvocationHandlerImpl的invoke方法。這是為啥呢?
======橫線之間的是程式碼跟分析的過程,不想看的朋友可以直接看結論======
以下程式碼來自:JDK1.8.0_92
既然生成代理物件是用的Proxy類的靜態方newProxyInstance,那麼我們就去它的原始碼裡看一下它到底都做了些什麼?
我們再進去getProxyClass0方法看一下:

真相還是沒有來到,繼續,看一下proxyClassCache
奧,原來用了一下快取啊

那麼它對應的get方法啥樣呢?


我們可以看到它呼叫了 supplier.get(); 獲取動態代理類,其中supplier是Factory,這個類定義在WeakCach的內部。
來瞅瞅,get裡面又做了什麼?

發現重點還是木有出現,但我們可以看到它呼叫了valueFactory.apply(key, parameter)方法:

通過看程式碼終於找到了重點:

那麼接下來我們也使用測試一下,使用這個方法生成的位元組碼是個什麼樣子:

我們用jd-jui 工具將生成的位元組碼反編譯:

這就是最終真正的代理類,它繼承自Proxy並實現了我們定義的Subject介面
也就是說:
這裡的subject實際是這個類的一個例項,那麼我們呼叫它的:
就是呼叫我們定義的InvocationHandlerImpl的 invoke方法:

======橫線之間的是程式碼跟分析的過程,不想看的朋友可以直接看結論=======

五、結論

到了這裡,終於解答了:
subject.SayHello(“jiankunking”)這句話時,為什麼會自動呼叫InvocationHandlerImpl的invoke方法?

因為JDK生成的最終真正的代理類,它繼承自Proxy並實現了我們定義的Subject介面,在實現Subject介面方法的內部,通過反射呼叫了InvocationHandlerImpl的invoke方法。

包含生成本地class檔案的demo:http://download.csdn.net/detail/xunzaosiyecao/9597474

通過分析程式碼可以看出Java 動態代理,具體有如下四步驟:

  1. 通過實現 InvocationHandler 介面建立自己的呼叫處理器;
  2. 通過為 Proxy 類指定 ClassLoader 物件和一組 interface 來建立動態代理類;
  3. 通過反射機制獲得動態代理類的建構函式,其唯一引數型別是呼叫處理器介面型別;
  4. 通過建構函式建立動態代理類例項,構造時呼叫處理器物件作為引數被傳入。

相關文章