理解java動態代理

      java動態代理是java語言的一項高級特性。在平時的項目開發中,可能很難遇到動態代理的案例。可是動態代理在不少框架中起着不可替代的做用,例如Spring的AOP。今天咱們就聊一聊java動態代理的實現原理。java

     jdk對於動態代理的支持主要依賴於兩個類:Proxy和InvocationHandler。咱們先看一下類圖。設計模式

 

     

  Subject類是主題類,定義了我要作什麼。咱們須要代理的類即實現Subject接口的RealSubject。數組

  1.InvocationHandler框架

  InvocationHandler接口是jdk提供的接口,這個接口只有一個方法jvm

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

      咱們先了解下InvocationHandler這個類是作什麼。如下是java docide

* <p>Each proxy instance has an associated invocation handler.
* When a method is invoked on a proxy instance, the method
* invocation is encoded and dispatched to the {@code invoke}
* method of its invocation handler.

  每個代理實例都會和一個invocation handler關聯,準確的說,每個proxy類都會持有一個InvocationHandler實例,而且將目標函數交給InvcationHandler實例去執行。InvocationHandler只有invoke()這個方法,這個方法即實際被調用的方法。無論代理調用的是何種方法,處理器被調用的必定是invoke()方法。下面咱們看看這個方法的參數。函數

  1. Object proxy。傳入的Subject引用,即咱們想要真正執行的目標對象。post

  2. Method method。Method是java reflection API的一部分。這裏傳入的method對象,是實際被調用的method方法。this

  3. Object[] args。這是方法調用時傳入的參數數組。spa

  瞭解invoke()方法後,讀者必定想知道,Subject的目標方法是怎麼被調用的呢?接下來咱們繼續瞭解Proxy類。

  2. Proxy

  接下來咱們瞭解下Proxy類是如何與InvocationHandler一共工做的。java doc中對Proxy的介紹以下:

* provides static methods for creating dynamic proxy
* classes and instances, and it is also the superclass of all
* dynamic proxy classes created by those methods.

  Proxy提供了一個靜態方法去建立動態代理類,最經常使用的就是下面這個方法了。

 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

  利用newProxyInstance能夠動態的建立所須要的代理對象,而且和與它關聯的InvocationHandler綁定。參數以下

  1. ClassLoader loader, 加載代理類的類加載器。

  2. Class<?>[] interfaces, 代理類實現的接口。建立的代理類對象,只能強轉爲該interfaces的子類。

  3. InvocationHandler h, 代理類所關聯的InvocationHandler。全部被代理的方法都會經過該InvocationHandler的invoke()方法執行。

  newProxyInstance方法是生成代理類的關鍵方法,代理類在程序運行的過程當中生成,於是叫作動態代理。

  3. 案例

  瞭解了這兩個最重要的類以後,咱們須要經過一個實例來幫助咱們更好的理解動態代理的運行機制。

  首先咱們建立一個Subject接口以及其實現類。

  步驟1. 定義Subject

 1 public interface Subject {  2 
 3     public void say(String str);  4 }  5 
 6 public class SubjectBean implements Subject {  7 
 8  @Override  9     public void say(String str) { 10  System.out.println(str); 11  } 12 }

   Subject的say方法是咱們須要代理的方法。在該方法的先後咱們不妨作一些額外的操做。接下來咱們定義咱們的InvocationHandler。

  步驟2. 定義InvocationHandler

 1 public class MyInvocationHandle implements InvocationHandler {  2 
 3  Subject subject;  4 
 5     public MyInvocationHandle(Subject subject) {  6         this.subject = subject;  7  }  8 
 9  @Override 10     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 11         System.out.println("pre"); 12  method.invoke(subject, args); 13         System.out.println("post"); 14         return null; 15  } 16 
17 }

  經過構造器,把被代理的對象傳入。

  步驟3. 定義生成代理類方法實現

public class Main { public static Subject getProxy(Subject subject){ return (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new MyInvocationHandle(subject)); } public static void main(String[] args) { Subject subject = new SubjectBean(); Subject proxy = getProxy(subject); proxy.say("hello"); } }

  執行main函數,最後輸出的結果爲:

    

 可見,say()函數真正method.invoke(subject, args)這裏完成的。在執行先後能夠加入任意代碼片斷,完成對say()方法的加強操做。

 4. debug

  咱們對main方法debug看看,proxy類究竟是什麼。以下圖

  

  com.sun.proxy.$Proxy()類,是Proxy.newProxyInstance被調用後在jvm運行時動態生成的一個對象,命名方式都是這樣的形式,以$開頭,proxy爲中,最後一個數字表示對象的標號。至於它爲何能夠轉爲Subject,是由於咱們在傳入的第二個參數中,規定了它的類型信息。

  這篇文章主要簡述了java動態代理的實現機制。若有錯誤之處,還望讀者多多指教。

 

  參考文獻:

 《Head First設計模式》

 

 

做者: mayday芋頭
本博客中未標明轉載的文章歸做者mayday芋頭和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。
相關文章
相關標籤/搜索