原文同步發表至我的博客【夜月歸途】html
原文連接:http://www.guitu18.com/se/java/2019-01-03/27.htmljava
本博客關於Java動態代理相關內容直達連接:ide
博客真的是好幾個月沒更了,2019新年第一篇,繼續深刻動態代理,前兩篇簡單分析了動態代理的實現原理以後,此次繼續深刻了解具體的實現方式,並手寫一套簡易的動態代理已增強理解;
先接上一篇代碼,這裏先上代碼說話,一個簡單案列,代理找對象和找工做:工具
JDK動態代理只能代理有接口的類,定義Persion接口 post
package com.guitu18.study.proxy; /** * @author zhangkuan * @email xianjian-mail@qq.com * @Date 2018/12/31 20:30 */ public interface Persion { /** * 找對象 * * @param condition 對另外一半的要求 * @return 表態 */ String findLove(String condition); /** * 找工做 */ void findWord(); }
實現接口的類:測試
package com.guitu18.study.proxy; /** * @author zhangkuan * @email xianjian-mail@qq.com * @Date 2018/12/31 20:13 */ public class ZhangKuan implements Persion { @Override public String findLove(String condition) { System.out.println(condition); return "葉青我愛你"; } @Override public void findWord() { System.out.println("我想找月薪15-25k的工做"); } }
代理類:ui
package com.guitu18.study.proxy.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @author zhangkuan * @email xianjian-mail@qq.com * @Date 2018/12/31 20:13 */ public class JDKProxy { public static Object getProxyInstance(final Object target) { return 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("開始事務..."); // 執行方法 Object invoke = method.invoke(target, args); // 方法執行後 System.out.println("提交/回滾事務..."); return invoke; } }); } }
代理測試類:this
package com.guitu18.study.proxy.jdk; import com.guitu18.study.proxy.Persion; import com.guitu18.study.proxy.ZhangKuan; import sun.misc.ProxyGenerator; import java.io.FileOutputStream; /** * @author zhangkuan * @email xianjian-mail@qq.com * @Date 2018/12/31 20:28 */ public class JDKProxyTest { public static void main(String[] args) { try { Persion persion = (Persion) JDKProxy.getProxyInstance(new ZhangKuan()); String love = persion.findLove("膚白貌美大長腿"); System.out.println(love); // 葉青我愛你 System.out.println(persion.getClass()); // class com.sun.proxy.$Proxy0 /** * 上面生成的代理對象字節碼 com.sun.proxy.$Proxy0 是在內存中的 * 這裏將其對象寫到文件中,經過反編譯查看 */ byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Persion.class}); FileOutputStream fos = new FileOutputStream("D://$Proxy0.class"); fos.write(bytes); System.out.println("文件寫入成功"); } catch (Exception e) { e.printStackTrace(); } } }
到這裏,代理任務已經能夠完成看;此處,咱們將JDK動態代理生成的代理類,經過流的形式保存到磁盤中了,如今使用idea自帶反編譯工具查看:url
import com.guitu18.study.proxy.Persion; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements Persion { private static Method m1; private static Method m2; private static Method m4; private static Method m3; private static Method m0; public $Proxy0(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); } } public final void findWord() throws { try { super.h.invoke(this, m4, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String findLove(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); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m4 = Class.forName("com.guitu18.study.proxy.Persion").getMethod("findWord"); m3 = Class.forName("com.guitu18.study.proxy.Persion").getMethod("findLove", Class.forName("java.lang.String")); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
看完這段代碼是否是恍然大悟的感受,生成的代理類繼承了Proxy類並實現了咱們的接口,並生成了被代理類的所有方法,包括toString、equals等等全部方法; 這裏生成的代理類繼承自Proxy,在Proxy中有這麼一段代碼:idea
/** * the invocation handler for this proxy instance. * @serial */ protected InvocationHandler h;
這就是爲何咱們在生成代理類的時候,要傳入的一個InvocationHandler的實現,咱們全部的代理加強代碼都寫在invoke()方法中,而最終代理類執行代理也是經過調用父類Proxy中的InvocationHandler接口的invoke()方法;
而咱們知道Java是單繼承的,因此代理類只能去實現咱們的接口以供咱們能夠將其強轉;
在咱們調用自身方法時,代理類巧妙的經過調用生成的同名方法而後調用super.h.invoke()方法,最終就到了咱們實現InvocationHandler時所寫的invoke()加強方法;在此時,method.invoke()才真正經過反射調用了咱們自身所寫的方法;這種極爲巧妙無侵入式的方法加強實在是妙,使人歎爲觀止;
這裏推薦一篇 勞夫子 的 [ ProxyGenerator生成代理類的字節碼文件解析 ];
分析的是ProxyGenerator.generateClassFile()字節碼生成原理,寫的很是好,值得好好看一下;
下一篇,經過手寫一套簡易的JDK動態代理的實現加強理解;