源码角度理解JDK动态代理的实现原理
来源:互联网 发布:内眼角后遗症知乎 编辑:程序博客网 时间:2024/06/06 00:36
在另一篇文章浅析AOP实现原理(2)JDK动态代理中我们了解了JDK动态代理的用法,但是光会用还不行,这篇文章我们来探讨几个第一次使用JDK动态代理时可能都会产生的疑问:
* 1、代理类对象究竟是如何生成的
* 2、invoke方法是何时被调用的
JDK如何动态生成代理类对象
在上一篇文章中,生成代理的方法为:
public static Object getProxy(Object object){ return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new ProxyClient(object)); }
我们来看看这个方法是如何实现的(以下非完整代码,只展示了部分重要代码)
//这个方法中传入了代理对象的类加载器,接口和相应的InvocationHandler对象(最终调用者触发其中的Invoke方法):public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler )throws IllegalArgumentException { //复制接口 final Class<?>[] intfs = interfaces.clone(); //生成代理类 Class<?> cl = getProxyClass0(loader, intfs); //根据构造函数来创建代理对象 final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); }
由此可知newProxyInstance
函数做了两件事:生成代理类的Class对象,然后用Class对象的构造函数创建代理对象,我们先来看看getProxyClass0
方法是如何生成代理类的
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { //判断接口数是否超标 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory return proxyClassCache.get(loader, interfaces); }
根据源码中的注释可知,JAVA使用WeakCache
对代理类进行了缓存,该缓存的定义如下
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
具体的缓存机制我们不去研究,只需要知道它将首先从缓存中查找是否存在,若未找到,会通过ProxyClassFactory
来创建代理类。行,那我们就再来看看ProxyClassFactory
的代码
private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { //所有代理类的前缀 private static final String proxyClassNamePrefix = "$Proxy"; //代理类类名后缀,是AtomicLong类型的唯一计数 private static final AtomicLong nextUniqueNumber = new AtomicLong(); //重写BiFunction接口的方法 @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { //创建一个IdentitiHashMap用来存放获取的类 Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { Class<?> interfaceClass = null; //根据接口的名称获取类 interfaceClass = Class.forName(intf.getName(), false, loader); //利用IdentityHashMap的特性判断是否有重复的类 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } //定义代理类包名 String proxyPkg = null; int accessFlags = Modifier.PUBLIC | Modifier.FINAL; //区分共有和非共有的接口并定义包名 for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } //默认包名为com.sun.proxy if (proxyPkg == null) { proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } //确定代理类名,默认为com.sun.proxy.$Proxy0(第二个代理类后缀为1,以此类推) long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; //生成代理类的二进制文件 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); //返回代理类 return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } }
由此可知,ProxyClassFactory
实现了BiFunction
接口,将类加载器和接口数组传入重写的apply()
方法中,其中主要做了三件事:
* 1、生成代理类的名称,默认为com.sun.proxy.$Proxy0
(数字标识不同的类,如
* 2、通过ProxyGenerator.generateProxyClass()
方法生成代理类的二进制文件
* 3、通过defineClass0()
方法产生对应名称的Class对象并返回
因此ProxyClassFactory
中我们主要关注生成二进制文件的ProxyGenerator.generateProxyClass()
方法:
//传入代理类名proxyName->var0,接口对应的Class对象interfaces->var1,标识接口是否为共有接口的accessFlags->var2public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) { //生成ProxyGenerator对象 ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2); //调用generateClassFile()方法生成类的二进制文件 final byte[] var4 = var3.generateClassFile(); //如果saveGeneratedFiled为true,将文件写入硬盘的IO操作 if (saveGeneratedFiles) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { int var1 = var0.lastIndexOf(46); Path var2; if (var1 > 0) { Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar)); Files.createDirectories(var3); var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class"); } else { var2 = Paths.get(var0 + ".class"); } Files.write(var2, var4, new OpenOption[0]); return null; }); } return var4; }
所以在ProxyGenerator.generateProxyClass()
方法中,首先调用了var3.generateClassFile()
方法产生二进制文件,然后将该文件写入硬盘或直接返回,那我们再来关注一下var3.generateClassFile()
(我保证这是最后一次)
private byte[] generateClassFile() { //将hashCode、equals、toString方法添加到proxyMethods这个map中 this.addProxyMethod(hashCodeMethod, Object.class); this.addProxyMethod(equalsMethod, Object.class); this.addProxyMethod(toStringMethod, Object.class); //获取接口类和个数 Class[] var1 = this.interfaces; int var2 = var1.length; int var3; Class var4; //遍历所有的接口 for(var3 = 0; var3 < var2; ++var3) { var4 = var1[var3]; //获得该接口中所有的方法 Method[] var5 = var4.getMethods(); int var6 = var5.length; //将方法添加到代理方法 for(int var7 = 0; var7 < var6; ++var7) { Method var8 = var5[var7]; this.addProxyMethod(var8, var4); } } Iterator var11, //中间省略验证步骤 Iterator var15; //生成代理类的构造函数并添加到methods,methods是一个List,它包含了代理类的方法信息 this.methods.add(this.generateConstructor()); var11 = this.proxyMethods.values().iterator(); while(var11.hasNext()) { var12 = (List)var11.next(); var15 = var12.iterator(); //将proxyMethod里的方法和变量存入fields和methods里 while(var15.hasNext()) { ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next(); this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10)); this.methods.add(var16.generateMethod()); } } //一些初始化 this.methods.add(this.generateStaticInitializer()); //后面主要是一些将methods和fields写成二进制流的文件操作 ······· return .......toByteArray(); } }
至此我们终于得到了代理类的二进制文件,再来理一理思路:
首先Proxy.newProxyInstance()
方法被调用,在该方法中,getProxyClass()
方法被调用,在getProxyClass
方法中先查找缓存中是否有代理类,若没有,则调用ProxyClassFactory
的apply()
方法来产生代理类,apply()
方法中先根据自定义的规则生成包名和类名进行拼接,然后调用ProxyGenerator.generateProxyClass()
方法生成代理类的二进制文件,而真正生成二进制文件的方法是其中的var3.generateClassFile()
方法,该方法中遍历所有接口,获得接口中的方法和变量信息,最后根据这些信息写成二进制文件,根据saveGeneratedFileds
判断是否写入硬盘,二进制文件返回到ProxyClassFactory
的apply()
方法中,再根据包名,类加载器,代理类的二进制文件生成代理类并返回。
看起来有点混乱,其实用一句话来说就是:通过java反射从接口中获取变量和方法信息来生成代理类的过程
invoke方法是何时被调用的
要知道invoke何时被调用,就必须先知道jdk动态代理为我们生成的文件究竟是什么,以下代码在指定路径输出文件:
@Test public void testProxy() throws IOException { String path = "D:/$Proxy1.class"; byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy1", KillerImpl.class.getInterfaces()); FileOutputStream out = new FileOutputStream(path); out.write(classFile); out.flush(); }
到D盘下用反编译工具获得Proxy1的源文件(不会反编译的点这里):
import com.lwl.aop.Killer;import java.lang.reflect.*;public final class $Proxy1 extends Proxy implements Killer{ //调用Proxy的构造函数注入invocationHandler public $Proxy1(InvocationHandler invocationhandler) { super(invocationhandler); } public final boolean equals(Object obj) { try { return ((Boolean)super.h.invoke(this, m1, new Object[] { obj })).booleanValue(); } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final String toString() { try { return (String)super.h.invoke(this, m2, null); } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final int hashCode() { try { return ((Integer)super.h.invoke(this, m0, null)).intValue(); } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } //的kill方法在此处被调用 public final void kill() { try { //调用Proxy中invocationHandler的invoke方法 super.h.invoke(this, m3, null); return; } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } private static Method m1; private static Method m2; private static Method m0; private static Method m3; static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m3 = Class.forName("com.lwl.aop.Killer").getMethod("kill", new Class[0]); } catch(NoSuchMethodException nosuchmethodexception) { throw new NoSuchMethodError(nosuchmethodexception.getMessage()); } catch(ClassNotFoundException classnotfoundexception) { throw new NoClassDefFoundError(classnotfoundexception.getMessage()); } }}
这个文件是继承自Proxy
并实现了Killer
的类,注入了invocationHandler
,包含equals
、toString
、hashCode
、kill
四个方法,在各个方法内部调用Proxy
的InvocationHandler
中的invoke
方法,至此我们终于弄明白了JDK动态代理的实现原理
部分参考JDK动态代理实现原理
- 源码角度理解JDK动态代理的实现原理
- JDK的动态代理实现原理理解
- 【动态代理】从源码实现角度剖析JDK动态代理
- JDK动态代理实现原理(源码解读)
- 深入理解 jdk 动态代理的实现
- 细说JDK动态代理的实现原理
- 细说JDK动态代理的实现原理
- 细说JDK动态代理的实现原理
- JDK动态代理的实现及原理
- 细说JDK动态代理的实现原理
- 细说JDK动态代理的实现原理
- 细说JDK动态代理的实现原理
- JDK动态代理的实现及原理
- JDK动态代理的实现及原理
- JDK动态代理的实现及原理
- JDK动态代理的实现及原理
- 细说JDK动态代理的实现原理
- 细说JDK动态代理的实现原理
- 委托代理模式
- 关于在Keil中在peripheral中显示对应的窗口
- 正确认识历史 开创美好未来 --南京大屠杀死难者国家公祭仪式
- Python单元测试框架 — unittest详解
- 【中国自动驾驶里程碑】北京出台全国首例自动驾驶新规,将推出第一批测试道路
- 源码角度理解JDK动态代理的实现原理
- VS 破解
- 麦子学院C++学习笔记
- 回合制游戏指令的执行机制
- css 属性选择器学习+css3 混合模式+css background
- day1
- nginx+lua+redis防刷,lua代码
- opencv3/C++霍夫圆/直线检测
- Android_黑马视频学习_day04