java動態代理是java語言的一項高級特性。在平時的項目開發中,可能很難遇到動態代理的案例。但是動態代理在很多框架中起著不可替代的作用,例如Spring的AOP。今天我們就聊一聊java動態代理的實現原理。
jdk對於動態代理的支持主要依賴於兩個類:Proxy和InvocationHandler。我們先看一下類圖。
Subject類是主題類,定義了我要做什麼。我們需要代理的類即實現Subject接口的RealSubject。
1.InvocationHandler
InvocationHandler接口是jdk提供的接口,這個接口只有一個方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
我們先了解下InvocationHandler這個類是做什麼。以下是java doc
* <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引用,即我們想要真正執行的目標對象。
2. Method method。Method是java reflection API的一部分。這裡傳入的method對象,是實際被調用的method方法。
3. Object[] args。這是方法調用時傳入的參數數組。
了解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方法是生成代理類的關鍵方法,代理類在程序運行的過程中生成,因而叫做動態代理。
了解了這兩個最重要的類之後,我們需要通過一個實例來幫助我們更好的理解動態代理的運行機制。
首先我們創建一個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芋頭 出處:http://www.cnblogs.com/maypattis/ 本博客中未標明轉載的文章歸作者mayday芋頭和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。