您好,登錄后才能下訂單哦!
這篇文章主要介紹了基于JDK動態(tài)代理原理是什么的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇基于JDK動態(tài)代理原理是什么文章都會有所收獲,下面我們一起來看看吧。
如果我們要為target類創(chuàng)建一個【JDK動態(tài)代理對象】,那么我們必須要傳入如下三個核心參數
加載target類的類加載器
target類實現(xiàn)的接口
InvocationHandler
為什么必須要這三個參數呢?之前使用動態(tài)代理的時候都是直接按接口要求傳這三個參數,但從來沒想過為什么?下面仔細去探究一下
【JDK動態(tài)代理】的核心其實是借助【Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)】方法,去創(chuàng)建的動態(tài)代理對象,我們這里也使用這個方法去創(chuàng)建一個簡單的【動態(tài)代理對象】以便于理解他的核心原理。
public interface Subject { /** * 接口方法 */ void doSomething(); /** * sayHello * * @param name name * @return string */ String sayHello(String name); }
實現(xiàn)Subject接口,并實現(xiàn)接口相關方法
public class RealSubject implements Subject { @Override public void doSomething() { System.out.println("RealSubject do something"); } @Override public String sayHello(String name) { System.out.println("RealSubject sayHello"); return "hello-" + name; } }
定義一個工廠類,該工廠類用于為target對象生產代理對象
該工廠類借助Proxy.newProxyInstance
來為目標對象創(chuàng)建代理對象
public class JdkDynamicProxyFactory { /** * 創(chuàng)建target類的代理對象 * 注意:當調用代理對象中的方法時,其實就是調用的InvocationHandler里面的invoke方法,然后在invoke方法里調用目標對象對應的方法 * * @param <T> 泛型 * @return 代理對象 */ public static <T> T getProxy(Object target) { // 創(chuàng)建代理實例,分別傳入:【加載target類的類加載器、target類實現(xiàn)的接口、InvocationHandler】 Object proxyInstance = Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("執(zhí)行目標方法前"); // 執(zhí)行目標方法 Object result = method.invoke(target, args); System.out.println("執(zhí)行目標方法后"); // 返回目標方法的執(zhí)行結果 return result; } }); // 返回代理對象 return (T) proxyInstance; } }
該測試類會將內存中的動態(tài)代理對象保存到磁盤上,以便于我們后續(xù)分析生成的動態(tài)代理類的具體結構
public class Client { public static void main(String[] args) { // 保存生成的代理類的字節(jié)碼文件 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); // 目標對象 RealSubject target = new RealSubject(); // 使用JDK動態(tài)代理為【target對象】創(chuàng)建代理對象 Subject proxy = JdkDynamicProxyFactory.getProxy(target); // 調用代理對象的方法 proxy.doSomething(); System.out.println("=====================華麗的分割線====================="); proxy.sayHello("wenpan"); } }
在上面一步中我們使用
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
將動態(tài)生成的代理保存到了磁盤上,下面我們就具體看看生成的代理類長什么樣
可以看到代理類【繼承了Proxy類】,并且【實現(xiàn)了目標接口Subject】,覆寫了接口中的【每一個方法】
這也說明了為什么JDK代理需要實現(xiàn)接口,因為Java是單繼承的,既然代理類繼承了Proxy類那么就無法再繼承其他類了
在代理類中將我們的【目標接口Subject】的【所有方法(包括object父類的方法)】都以【靜態(tài)屬性的形式】保存了起來(主要是為了方便后面的【反射調用】)
在調用動態(tài)代理對象的某個方法時(比如:調用doSomething方法),實質上是調用的【Proxy類】的【h屬性】的invoke方法
所以我們要重點去看看這個【Proxy.h】到底是個什么鬼?其實他就是創(chuàng)建代理對象是我們傳入的【InvocationHandler】
// 1、代理類首先繼承了Proxy類(這也說明了為什么JDK代理需要實現(xiàn)接口,因為Java是單繼承的),并且實現(xiàn)了目標接口Subject public final class $Proxy0 extends Proxy implements Subject { // 2、可以看到,在代理類中將我們的【目標接口Subject】的【所有方法(包括object父類的方法)】都以【靜態(tài)屬性的形式】保存了起來 private static Method m1; private static Method m3; private static Method m4; private static Method m2; private static Method m0; // 以靜態(tài)代碼塊的形式為屬性賦值 static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("com.stone.design.mode.proxy.jdk.Subject").getMethod("doSomething"); m4 = Class.forName("com.stone.design.mode.proxy.jdk.Subject").getMethod("sayHello", Class.forName("java.lang.String")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } public $Proxy0(InvocationHandler var1) throws { super(var1); } // 3、object父類的equals方法 public final boolean equals(Object var1) throws { try { // 這里的supper是指的Proxy類,調用【Proxy類】的【h屬性】的invoke方法執(zhí)行 // 重點:【注意這里的super.h】其實就是我們創(chuàng)建代理對象是傳入的【InvocationHandler】,不信往后面看 // 這里也就體現(xiàn)了創(chuàng)建代理對象時為什么需要傳入【InvocationHandler】,以及為什么調用代理對象的方法時都是執(zhí)行的invoke方法 return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } // 4、目標接口的方法 public final void doSomething() throws { try { // 調用了【Proxy.h屬性】的invoke方法 // 注意這里的super.h】其實就是我們創(chuàng)建代理對象是傳入的【InvocationHandler】,不信往后面看 super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } // 5、目標接口的方法 public final String sayHello(String var1) throws { try { // 調用了【Proxy.h屬性】的invoke方法 // 注意這里的super.h】其實就是我們創(chuàng)建代理對象是傳入的【InvocationHandler】,不信往后面看 return (String)super.h.invoke(this, m4, 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); } } 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); } } }
在上面的第三點中我們看到,執(zhí)行代理對象的方法的時候其實質是調用的【super.h.invoke方法】也就是【Proxy.h.invoke方法】,那么我們仔細看看【Proxy.h】到底是什么鬼
下面的源代碼我做了一些刪減,只留下了最核心的部分
通過下面代碼我們就明確了使用【newProxyInstance】方法創(chuàng)建代理對象時所做的幾件事情
首先通過【目標接口】 + 【類加載器】創(chuàng)建一個Proxy類的【Class對象】
然后通過這個【Class對象】獲取到他的有參構造器,并且傳入我們自定義的【InvocationHandler】作為構造函數參數,并且通過反射的方式調用有參構造器創(chuàng)建一個【Proxy對象】
在【Proxy的有參構造器】中,會將傳入的【InvocationHandler】保存到 【h屬性】上(方便后面的supper.h.invoke調用)
代理對象創(chuàng)建完畢
public class Proxy implements java.io.Serializable { // h屬性,保存我們傳遞進來的InvocationHandler protected InvocationHandler h; // 【有參構造器】注意這里的參數 protected Proxy(InvocationHandler h) { Objects.requireNonNull(h); this.h = h; } // 生成代理對象的方法 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException{ // 1、InvocationHandler強制不允許為空 Objects.requireNonNull(h); // 獲取到目標接口 final Class<?>[] intfs = interfaces.clone(); /* * Look up or generate the designated proxy class. * 2、獲取到代理類的Class對象(也就是Proxy) */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. * 通過反射執(zhí)行 cl 的有參構造,也就是下面這個,可以看到通過反射執(zhí)行Proxy有參構造, * 將InvocationHandler賦值到了h屬性上 */ try { // 3、獲取到有參構造器 final Constructor<?> cons = cl.getConstructor(constructorParams); // 4、通過構造器來創(chuàng)建一個代理對象并返回,這里傳入的參數h 就是我們的【InvocationHandler】 return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { // 省略.... } } }
現(xiàn)在再來看上面拋出的三個問題!
為什么創(chuàng)建代理對象時需要傳入如下三個參數:
加載target類的類加載器
target類實現(xiàn)的接口
InvocationHandler
因為動態(tài)代理類也是類,我們普通的類是從【磁盤上的.class文件(也可以是其他地方,比如網絡上)】里加載而來,而動態(tài)代理類則是在【運行過程中動態(tài)生成的類】。
那么既然是類那么他就一定要【被類加載器加載后】才能被我們的【Java虛擬機】識別。
所以我們會傳入【加載target類的類加載器】,用該類加載器來加載【動態(tài)生成的代理類】
為啥要傳入【target類實現(xiàn)的接口】呢?直接【繼承目標類】不行嗎?肯定不行
從上面的【動態(tài)生成的代理類的結構】來看,代理類繼承了Proxy類,由于【Java是單繼承】的,所以無法再通過繼承的方式來繼承【目標類】了。
所以動態(tài)代理類需要【實現(xiàn)目標接口】,來重寫接口的方法
從【動態(tài)生成的代理類的結構】可以看出,我們傳入的InvocationHandler
最終會被作為一個屬性保存到Proxy對象的【h屬性】上
并且【動態(tài)代理對象】會覆寫【目標接口的所有方法】,在方法中會使用 supper.h.invoke
的方式調用InvocationHandler的invoke方法,所以我們需要傳入InvocationHandler
動態(tài)代理的每個方法調用都會先走InvocationHandler.invoke()
方法
關于“基于JDK動態(tài)代理原理是什么”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“基于JDK動態(tài)代理原理是什么”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業(yè)資訊頻道。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。