不仅知其然,还得知其所以然。既然JDK 动态代理功能如此强大,那么他是如何实现的呢?
我么都知道 JDK 动态代理采用字节重组,重组生成对象来替代原始对象,以达到动态代理的目的。JDK 动态代理生成对象的步骤如下:
(1)获取被代理对象的引用,并且获取他的所有接口,反射获取。
(2)JDK 动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口。
(3)动态生成的 Jave 代码,新加的业务逻辑方法由一定的逻辑代码调用。
(4)编译新生成的 Java 代码 .class 文件。
(5)重新加载到JVM中运行。
以上过程就叫做字节码重组。JDK 中有一个规范,在ClassPath 下只要是 $ 开头的 .class 文件,一般都是自动生成的。那么我们可以将内存中的对象字节码通过文件流输出到一个新的 .calss 文件,然后利用反编译工具查看其源代码。
package com.xq.design.proxy; import com.xq.design.proxy.dynamicproxy.jdkproxy.Customer; import com.xq.design.proxy.dynamicproxy.jdkproxy.JDKMeipo; import com.xq.design.proxy.dynamicproxy.jdkproxy.Person; import org.junit.jupiter.api.Test; import sun.misc.ProxyGenerator; import java.io.FileOutputStream; public class JDKProxyTest { @Test void JDKProxyTest(){ try{ Person obj =(Person) new JDKMeipo().getInstance(new Customer()); obj.findLove(); //通过反编译工具可以查看源代码 byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Person.class}); FileOutputStream os = new FileOutputStream("F://$Proxy0.class"); os.write(bytes); os.close(); }catch (Exception e){ e.printStackTrace(); } } }
运行以上代码,在F盘下找到 $Proxy0.class 文件。使用反编译工具反编译后得到 $Oroxy.java 文件,打开看到如下内容
// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://kpdus.tripod.com/jad.html // Decompiler options: packimports(3) fieldsfirst ansi space import com.xq.design.proxy.dynamicproxy.jdkproxy.Person; import java.lang.reflect.*; public final class $Proxy0 extends Proxy implements Person { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler invocationhandler) { super(invocationhandler); } public final boolean equals(Object obj) { try { return ((Boolean)super.h.invoke(this, m1, new Object[] { obj })).booleanValue(); } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final void findLove() { try { super.h.invoke(this, m3, null); return; } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final String toString() { try { return (String)super.h.invoke(this, m2, null); } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final int hashCode() { try { return ((Integer)super.h.invoke(this, m0, null)).intValue(); } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("com.xq.design.proxy.dynamicproxy.jdkproxy.Person").getMethod("findLove", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } catch (NoSuchMethodException nosuchmethodexception) { throw new NoSuchMethodError(nosuchmethodexception.getMessage()); } catch (ClassNotFoundException classnotfoundexception) { throw new NoClassDefFoundError(classnotfoundexception.getMessage()); } } }
发现 $Proxy0 继承了Proxy 类,同时还实现了 Person 接口,而且重写了 findLove 方法。在静态块中用反射找到了目标对象的所有方法,而且保存了所有方法的引用,重写的方法用反射调用目标对象的方法。这些代码是JDK帮我们是生成的。
我们也可以不依赖 JDK ,自己来动态生成源代码、动态完成编译,然后替代目标对象并执行。