java动态代理原理及解析 叁歲伎倆 2022-05-10 10:14 184阅读 0赞 ## **代理:设计模式** ## 代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。 通过代理层这一中间层,有效的控制对于真实委托类对象的直接访问,同时可以实现自定义的控制策略(Spring的AOP机制),设计上获得更大的灵活性。 ## **java动态代理的类和接口(jdk1.6源码)** ## **1,java.lang.reflect.Proxy**:动态代理机制的主类,提供一组静态方法为一组接口动态的生成对象和代理类。 // 方法 1: 该方法用于获取指定代理对象所关联的调用处理器 public static InvocationHandler getInvocationHandler(Object proxy) // 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象 public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) // 方法 3:该方法用于判断指定类对象是否是一个动态代理类 public static boolean isProxyClass(Class<?> cl) // 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) **2,java.lang.reflect.InvocationHandler**:调用处理器接口,自定义invokle方法,用于实现对于真正委托类的代理访问。 /** 该方法负责集中处理动态代理类上的所有方法调用。 第一个参数既是代理类实例, 第二个参数是被调用的方法对象 第三个方法是调用参数。 调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; **3,java.lang.ClassLoader**:类装载器类,将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy类与普通类的唯一区别就是**其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中**。 每次生成动态代理类对象时都需要指定一个类装载器对象:newProxyInstance()方法第一个参数 ## **动态代理机制** ## java动态代理创建对象的过程为如下步骤: 1,通过实现 InvocationHandler 接口创建自己的调用处理器; // InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发 // 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用 InvocationHandler handler = new InvocationHandlerImpl(..); 2,通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类; // 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象 Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); 3,通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型; // 通过反射从生成的类对象获得构造函数对象 Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); 4,通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。 // 通过构造函数对象创建动态代理类实例 Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler }); 为了简化对象创建过程,Proxy类中的newProxyInstance方法封装了2~4,只需两步即可完成代理对象的创建。 // InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发 InvocationHandler handler = new InvocationHandlerImpl(..); // 通过 Proxy 直接创建动态代理类实例 Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, new Class[] { Interface.class }, handler ); > **动态代理的注意点:** > 1,包:代理接口是public,则代理类被定义在顶层包(package为空),否则(default),代理类被定义在该接口所在包, > > ![这里写图片描述][20160906180608152] > > 2,生成的代理类为public final,不能被继承, > > 3,类名:格式是“$ProxyN”,N是逐一递增的数字,代表Proxy被第N次动态生成的代理类,要注意,对于同一组接口(接口的排列顺序也相同),不会重复创建动态代理类,而是返回一个先前已经创建并缓存了的代理类对象。提高了效率。 > > ![这里写图片描述][20160906180648185] > > 4,类继承关系: > > ![这里写图片描述][image002.png] > Proxy 类是它的父类,这个规则适用于所有由 Proxy 创建的动态代理类。(也算是java动态代理的一处缺陷,java不支持多继承,所以无法实现对class的动态代理,只能对于Interface的代理)而且该类还实现了其所代理的一组接口,这就是为什么它能够被安全地类型转换到其所代理的某接口的根本原因。 > > 5,代理类的根类 java.lang.Object 中有三个方法也同样会被分派到调用处理器的 invoke 方法执行,它们是 hashCode,equals 和 toString, > 代码在反编译中 > ![这里写图片描述][20160906181138127] ### **一个动态代理的demo** ### 其中,bind方法中的newProxyInstanc方法,就是生成一个代理对象,第一个参数是类加载器,第二个参数是真实委托对象所实现的的接口(代理对象挂在那个接口下),第三个参数this代表当前HelloServiceProxy类,换句话说是使用HelloServiceProxy作为对象的代理。 invoke方法有三个参数:第一个proxy是代理对象,第二个是当前调用那个方法,第三个是方法的参数。 public class ProxyTest { public static void main(String[] args) { HelloServiceProxy proxy = new HelloServiceProxy(); HelloService service = new HelloServiceImpl(); //绑定代理对象。 service = (HelloService) proxy.bind(service, new Class[] {HelloService.class}); //这里service经过绑定,就会进入invoke方法里面了。 service.sayHello("张三"); } } 测试结果: ############我是JDK动态代理################ 我准备说hello。 hello 张三 我说过hello了 ## **源码跟踪** ## **Proxy 类** // 映射表:用于维护类装载器对象到其对应的代理类缓存 private static Map loaderToCache = new WeakHashMap(); // 标记:用于标记一个动态代理类正在被创建中 private static Object pendingGenerationMarker = new Object(); // 同步表:记录已经被创建的动态代理类类型,主要被方法 isProxyClass 进行相关的判断 private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap()); // 关联的调用处理器引用 protected InvocationHandler h; **Proxy 静态方法 newProxyInstance** public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { // 检查 h 不为空,否则抛异常 if (h == null) { throw new NullPointerException(); } // 获得与制定类装载器和一组接口相关的代理类类型对象 /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, interfaces); // 通过反射获取构造函数对象并生成代理类实例 /* * Invoke its constructor with the designated invocation handler. */ try { final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; SecurityManager sm = System.getSecurityManager(); if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { // create proxy instance with doPrivilege as the proxy class may // implement non-public interfaces that requires a special permission return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return newInstance(cons, ih); } }); } else { return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } } private static Object newInstance(Constructor<?> cons, InvocationHandler h) { try { return cons.newInstance(new Object[] {h} ); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString()); } } } 动态代理真正的关键是在 **getProxyClass0** 方法, ### **getProxyClass0方法分析** ### 方法分为四个步骤: 1,对这组接口进行一定程度的安全检查 检查接口类对象是否对类装载器可见并且与类装载器所能识别的接口类对象是完全相同的,还会检查确保是 interface 类型而不是 class 类型。 ![这里写图片描述][20160906182236131] 2,从 loaderToCache 映射表中获取以类装载器对象为关键字所对应的缓存表,如果不存在就创建一个新的缓存表并更新到 loaderToCache。 loaderToCache存放键值对(接口名字列表,动态生成的代理类的类对象引用)。当代理类正在被创建时它会临时保存(接口名字列表,pendingGenerationMarker)。标记 pendingGenerationMarke 的作用是通知后续的同类请求(接口数组相同且组内接口排列顺序也相同)代理类正在被创建,请保持等待直至创建完成。 /* * Find or create the proxy class cache for the class loader. */ Map cache; synchronized (loaderToCache) { cache = (Map) loaderToCache.get(loader); if (cache == null) { cache = new HashMap(); loaderToCache.put(loader, cache); } } 。。。。。 do { // 以接口名字列表作为关键字获得对应 cache 值 Object value = cache.get(key); if (value instanceof Reference) { proxyClass = (Class) ((Reference) value).get(); } if (proxyClass != null) { // 如果已经创建,直接返回 return proxyClass; } else if (value == pendingGenerationMarker) { // 代理类正在被创建,保持等待 try { cache.wait(); } catch (InterruptedException e) { } // 等待被唤醒,继续循环并通过二次检查以确保创建完成,否则重新等待 continue; } else { // 标记代理类正在被创建 cache.put(key, pendingGenerationMarker); // break 跳出循环已进入创建过程 break; } while (true); 3,动态创建代理类的class对象 /** * Choose a name for the proxy class to generate. */ long num; synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; } String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Verify that the class loader hasn't already * defined a class with the chosen name. */ // 动态地生成代理类的字节码数组 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); try { // 动态地定义新生成的代理类 proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); } // 把生成的代理类的类对象记录进 proxyClasses 表 proxyClasses.put(proxyClass, null); 首先根据规则(接口public与否),生成代理类的名称,$ProxyN格式,然后动态生成代理类。 所有的代码生成的工作都由 ProxyGenerator 所完成了,该类在rt.jar中,需要反编译 public static byte[] generateProxyClass(final String name, Class[] interfaces) { ProxyGenerator gen = new ProxyGenerator(name, interfaces); // 这里动态生成代理类的字节码,由于比较复杂就不进去看了 final byte[] classFile = gen.generateClassFile(); // 如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上 if (saveGeneratedFiles) { java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Void>() { public Void run() { try { FileOutputStream file = new FileOutputStream(dotToSlash(name) + ".class"); file.write(classFile); file.close(); return null; } catch (IOException e) { throw new InternalError( "I/O exception saving generated file: " + e); } } }); } // 返回代理类的字节码 return classFile; } 4,代码生成过程进入结尾部分,根据结果更新缓存表,如果成功则将代理类的类对象引用更新进缓存表,否则清楚缓存表中对应关键值,最后唤醒所有可能的正在等待的线程。 finally { /* * We must clean up the "pending generation" state of the proxy * class cache entry somehow. If a proxy class was successfully * generated, store it in the cache (with a weak reference); * otherwise, remove the reserved entry. In all cases, notify * all waiters on reserved entries in this cache. */ synchronized (cache) { if (proxyClass != null) { cache.put(key, new WeakReference(proxyClass)); } else { cache.remove(key); } cache.notifyAll(); } } return proxyClass; ## **InvocationHandler解析** ## 代码参考:[http://rejoy.iteye.com/blog/1627405][http_rejoy.iteye.com_blog_1627405] 通过getProxyClass0方法中生成具体的class文件的过程,定义path,讲class文件写到指定的磁盘中,反编译生成的代理class文件。 发现在静态代码块中获取了的方法有:Object中的equals方法、Object中的hashCode方法、Object中toString方法 , **以及invoke的接口方法。** ## **后语** ## 至此,JDK是动态生成代理类,并通过调用解析器,执行接口实现的方法的原理已经一目了然。动态代理加上反射,是很多框架的基础。比如Spring的AOP机制,自定义前置后置通知等控制策略,以及mybatis中的运用反射和动态代理来实现插件技术等等 \--------------------- 本文来自 简单世界 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/Scplove/article/details/52451899?utm\_source=copy [20160906180608152]: /images/20220510/4954e1b8ef6741e19b93c3cc4b24de76.png [20160906180648185]: /images/20220510/47d64325bc0b4a2fb3fd70e5f0edb822.png [image002.png]: /images/20220510/fc95b6097b8a4728a2b90ee9e1650c95.png [20160906181138127]: /images/20220510/c8a712c1ba4c446389ed017d39ddf3ee.png [20160906182236131]: /images/20220510/36b39b795b1b42b98ec89c5c50ec3093.png [http_rejoy.iteye.com_blog_1627405]: http://rejoy.iteye.com/blog/1627405
相关 Java动态代理实现原理及应用场景 Java动态代理是一种在运行时通过Java语言动态生成的代码,主要用于实现对现有类功能的扩展或隐藏。 实现原理: 1. **代理接口定义**:首先需要一个目标接口(被代理类的 待我称王封你为后i/ 2024年09月11日 16:36/ 0 赞/ 13 阅读
相关 Java动态代理深入理解:问题示例及解析 在Java中,动态代理是一个强大的工具,可以让我们创建行为动态的类。以下是动态代理的一些关键概念以及一个示例问题和解析。 1. 接口与实现: 在Java中,我们通过接口 系统管理员/ 2024年09月06日 07:30/ 0 赞/ 15 阅读
相关 java基础(十八)----- java动态代理原理源码解析 关于Java中的动态代理,我们首先需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理。 静态代理 1、静态代 ╰+哭是因爲堅強的太久メ/ 2022年10月03日 00:50/ 0 赞/ 106 阅读
相关 Java 动态代理实现解析 Java动态代理只能针对接口进行动态代理。如果需要对类进行实现代理可以使用:CGLIB,ASM等相关的操作字节码实现(在这里先只介绍下SUN 基于接口动态代理的实现)。 代码 小鱼儿/ 2022年08月26日 12:15/ 0 赞/ 138 阅读
相关 java动态代理原理及结构 代理:设计模式 代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被 喜欢ヅ旅行/ 2022年06月10日 13:59/ 0 赞/ 144 阅读
相关 java动态代理原理及解析 转载自 http://blog.csdn.net/scplove/article/details/52451899 代理:设计模式 代理模式是一种常用的设计模式,其目的 淩亂°似流年/ 2022年06月01日 13:39/ 0 赞/ 181 阅读
相关 java动态代理原理及解析 代理:设计模式 代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被 叁歲伎倆/ 2022年05月10日 10:14/ 0 赞/ 185 阅读
相关 静态代理和动态代理原理及实现 静态代理(Static Proxy)和动态代理(Dynamic Proxy) 静态代理 jdk动态代理 CGLIB动态代理 静态代理 静态代理要 ゞ 浴缸里的玫瑰/ 2021年12月14日 14:25/ 0 赞/ 214 阅读
相关 java AOP使用及jdk动态代理原理 简介 AOP(面向切面编程)可以说是OOP的补充和完善。它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个一个可重用模块,并将其命名 拼搏现实的明天。/ 2021年11月19日 12:20/ 0 赞/ 212 阅读
还没有评论,来说两句吧...