微信搜索:碼農StayUp
主頁地址:https://gozhuyinglong.github.io
源碼分享:https://github.com/gozhuyinglong/blog-demosjava
JDK動態代理是指:代理類實例在程序運行時,由JVM根據反射機制動態的生成。也就是說代理類不是用戶本身定義的,而是由JVM生成的。git
因爲其原理是經過Java反射機制實現的,因此在學習前,要對反射機制有必定的瞭解。傳送門:Java反射機制:跟着代碼學反射github
下面是本篇講述內容:數組
JDK動態代理有兩大核心類,它們都在Java的反射包下(java.lang.reflect
),分別爲InvocationHandler
接口和Proxy
類。緩存
代理實例的調用處理器須要實現
InvocationHandler
接口,而且每一個代理實例都有一個關聯的調用處理器。當一個方法在代理實例上被調用時,這個方法調用將被編碼並分派到其調用處理器的invoke
方法上。微信
也就是說,咱們建立的每個代理實例都要有一個關聯的InvocationHandler
,而且在調用代理實例的方法時,會被轉到InvocationHandler
的invoke
方法上。ide
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
該invoke
方法的做用是:處理代理實例上的方法調用並返回結果。函數
其有三個參數,分別爲:學習
Method
實例。Object
數組,是在代理實例上的方法調用中傳遞的參數值。若是接口方法爲無參,則該值爲null。其返回值爲:調用代理實例上的方法的返回值。this
Proxy
類提供了建立動態代理類及其實例的靜態方法,該類也是動態代理類的超類。
代理類具備如下屬性:
Proxy
類。InvocationHandler
的實現,用於設置代理實例的調用處理器。Proxy
提供了兩個靜態方法,用於獲取代理對象。
用於獲取代理類的Class
對象,再經過調用構造函數建立代理實例。
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException
該方法有兩個參數:
Class
對象數組。返回值爲動態代理類的Class
對象。
用於建立一個代理實例。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
該方法有三個參數:
Class
對象數組。返回值爲指定接口的代理類的實例。
Proxy
類主要用來獲取動態代理對象,InvocationHandler
接口主要用於方法調用的約束與加強。
上一章中已經介紹了獲取代理實例的兩個靜態方法,如今經過代碼示例來演示具體實現。
JDK動態代理是基於接口的,咱們建立一個接口及其實現類。
Foo接口:
public interface Foo { String ping(String name); }
Foo接口的實現類RealFoo:
public class RealFoo implements Foo { @Override public String ping(String name) { System.out.println("ping"); return "pong"; } }
建立一個InvocationHandler
接口的實現類MyInvocationHandler。該類的構造方法參數爲要代理的目標對象。
invoke
方法中的三個參數上面已經介紹過,經過調用method
的invoke
方法來完成方法的調用。
這裏一時看不懂不要緊,後面源碼解析章節會進行剖析。
public class MyInvocationHandler implements InvocationHandler { // 目標對象 private final Object target; public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("proxy - " + proxy.getClass()); System.out.println("method - " + method); System.out.println("args - " + Arrays.toString(args)); return method.invoke(target, args); } }
具體實現步驟以下:
@Test public void test1() throws Exception { Foo foo = new RealFoo(); // 根據類加載器和接口數組獲取代理類的Class對象 Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class); // 經過Class對象的構造器建立一個實例(代理類的實例) Foo fooProxy = (Foo) proxyClass.getConstructor(InvocationHandler.class) .newInstance(new MyInvocationHandler(foo)); // 調用 ping 方法,並輸出返回值 String value = fooProxy.ping("楊過"); System.out.println(value); }
輸出結果:
proxy - class com.sun.proxy.$Proxy4 method - public abstract java.lang.String io.github.gozhuyinglong.proxy.Foo.ping(java.lang.String) args - [楊過] ping pong
經過輸出結果能夠看出:
$Proxy
開頭的。經過這種方法是最簡單的,也是推薦使用的,經過該方法能夠直接獲取代理對象。
注:其實該方法後臺實現實際與上面使用getProxyClass方法的過程同樣。
@Test public void test2() { Foo foo = new RealFoo(); // 經過類加載器、接口數組和調用處理器,建立代理類的實例 Foo fooProxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[]{Foo.class}, new MyInvocationHandler(foo)); String value = fooProxy.ping("小龍女"); System.out.println(value); }
其實InvocationHander
接口也不用建立一個實現類,可使用Lambad表達式進行簡化的實現,以下代碼:
@Test public void test3() { Foo foo = new RealFoo(); Foo fooProxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[]{Foo.class}, (proxy, method, args) -> method.invoke(foo, args)); String value = fooProxy.ping("雕兄"); System.out.println(value); }
JVM爲咱們自動生成的代理類究竟是什麼樣子的呢?下面咱們先來生成一下,再來看裏面的構造。
JVM默認不建立該.class文件,須要增長一個啓動參數: -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
在IDEA中點擊【Edit Configurations...】,打開 Run/Debug Configurations 配置框。
將上面啓動參數加到【VM options】中,點擊【OK】便可。
再次運行代碼,會在項目中的【com.sun.proxy】目錄中找到這個.class文件,我這裏是「$Proxy4.class」
在Proxy
類中有個ProxyClassFactory
靜態內部類,該類主要做用就是生成靜態代理的。
其中有一段代碼ProxyGenerator.generateProxyClass
用來生成代理類的.class文件。
其中變量saveGeneratedFiles
即是引用了此啓動參數的值。將該啓動參數配置爲true
會生成.class文件。
神祕的面紗即將揭露,前面不少未解之迷在這裏能夠找到答案!
打開這個$Proxy
文件,我這裏生成的是$Proxy4
,下面是內容:
// 該類爲final類,其繼承了Proxy類,並實現了被代理接口Foo public final class $Proxy4 extends Proxy implements Foo { // 這4個Method實例,表明了本類實現的4個方法 private static Method m1; private static Method m2; private static Method m3; private static Method m0; // 靜態代碼塊根據反射獲取這4個方法的Method實例 static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("io.github.gozhuyinglong.proxy.Foo").getMethod("ping"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } // 一個公開的構造函數,參數爲指定的 InvocationHandler public $Proxy4(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } // Foo接口的實現方法,最終調用了 InvocationHandler 中的 invoke 方法 public final String ping(String var1) throws { try { return (String)super.h.invoke(this, m3, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } }
經過該文件能夠看出:
Proxy
類,其主要目的是爲了傳遞InvocationHandler
。InvocationHandler
,並將參數傳遞到父類Proxy
中。InvocationHandler
中的invoke
方法,並將代理類自己、Method實例、入參三個參數進行傳遞。這也是爲何調用代理類中的方法時,總會分派到InvocationHandler
中的invoke
方法的緣由。咱們從Proxy
類爲咱們提供的兩個靜態方法開始getProxyClass
和newProxyInstance
。上面已經介紹了,這兩個方法是用來建立代理類及其實例的,下面來看源碼。
經過上面源碼能夠看出,這兩個方法最終都會調用getProxyClass0
方法來生成代理類的Class
對象。只不過newProxyInstance
方法爲咱們建立好了代理實例,而getProxyClass
方法須要咱們本身建立代理實例。
下面來看這個統一的入口:getProxyClass0
從源碼和註解能夠看出:
ProxyClassFactory
建立代理類。(代理類會被緩存一段時間。)這裏簡單介紹一下WeakCache<K, P, V>
類,該類主要是爲代理類進行緩存的。獲取代理類時,會首先從緩存中獲取,若沒有會調用ProxyClassFactory
類進行建立,建立好後會進行緩存。
ProxyClassFactory
是Proxy
類的一個靜態內部類,該類用於生成代理類。下圖是源碼的部份內容:
$Proxy
,後綴是一個數字。ProxyGenerator.generateProxyClass
來生成指定的代理類。defineClass0
方法是一個native
方法,負責字節碼加載的實現,並返回對應的Class
對象。爲了便於記錄,將代理類的生成過程整理成了一張圖。
完整代碼請訪問個人Github,若對你有幫助,歡迎給個⭐,感謝~~