java动态代理原理及解析

叁歲伎倆 2022-05-10 10:14 394阅读 0赞

代理:设计模式

代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

通过代理层这一中间层,有效的控制对于真实委托类对象的直接访问,同时可以实现自定义的控制策略(Spring的AOP机制),设计上获得更大的灵活性。

java动态代理的类和接口(jdk1.6源码)

1,java.lang.reflect.Proxy:动态代理机制的主类,提供一组静态方法为一组接口动态的生成对象和代理类。

  1. // 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
  2. public static InvocationHandler getInvocationHandler(Object proxy)
  3. // 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
  4. public static Class<?> getProxyClass(ClassLoader loader,
  5. Class<?>... interfaces)
  6. // 方法 3:该方法用于判断指定类对象是否是一个动态代理类
  7. public static boolean isProxyClass(Class<?> cl)
  8. // 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
  9. public static Object newProxyInstance(ClassLoader loader,
  10. Class<?>[] interfaces,InvocationHandler h)

2,java.lang.reflect.InvocationHandler:调用处理器接口,自定义invokle方法,用于实现对于真正委托类的代理访问。

  1. /**
  2. 该方法负责集中处理动态代理类上的所有方法调用。
  3. 第一个参数既是代理类实例,
  4. 第二个参数是被调用的方法对象
  5. 第三个方法是调用参数。
  6. 调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
  7. */
  8. public Object invoke(Object proxy, Method method, Object[] args)
  9. throws Throwable;

3,java.lang.ClassLoader:类装载器类,将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy类与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中
每次生成动态代理类对象时都需要指定一个类装载器对象:newProxyInstance()方法第一个参数

动态代理机制

java动态代理创建对象的过程为如下步骤:
1,通过实现 InvocationHandler 接口创建自己的调用处理器;

  1. // InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
  2. // 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
  3. InvocationHandler handler = new InvocationHandlerImpl(..);

2,通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;

  1. // 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象
  2. Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });

3,通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

  1. // 通过反射从生成的类对象获得构造函数对象
  2. Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });

4,通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

  1. // 通过构造函数对象创建动态代理类实例
  2. Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });

为了简化对象创建过程,Proxy类中的newProxyInstance方法封装了2~4,只需两步即可完成代理对象的创建。

  1. // InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
  2. InvocationHandler handler = new InvocationHandlerImpl(..);
  3. // 通过 Proxy 直接创建动态代理类实例
  4. Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,
  5. new Class[] { Interface.class },
  6. handler );

动态代理的注意点:
1,包:代理接口是public,则代理类被定义在顶层包(package为空),否则(default),代理类被定义在该接口所在包,

这里写图片描述

2,生成的代理类为public final,不能被继承,

3,类名:格式是“$ProxyN”,N是逐一递增的数字,代表Proxy被第N次动态生成的代理类,要注意,对于同一组接口(接口的排列顺序也相同),不会重复创建动态代理类,而是返回一个先前已经创建并缓存了的代理类对象。提高了效率。

这里写图片描述

4,类继承关系:

这里写图片描述
Proxy 类是它的父类,这个规则适用于所有由 Proxy 创建的动态代理类。(也算是java动态代理的一处缺陷,java不支持多继承,所以无法实现对class的动态代理,只能对于Interface的代理)而且该类还实现了其所代理的一组接口,这就是为什么它能够被安全地类型转换到其所代理的某接口的根本原因。

5,代理类的根类 java.lang.Object 中有三个方法也同样会被分派到调用处理器的 invoke 方法执行,它们是 hashCode,equals 和 toString,
代码在反编译中
这里写图片描述

一个动态代理的demo

其中,bind方法中的newProxyInstanc方法,就是生成一个代理对象,第一个参数是类加载器,第二个参数是真实委托对象所实现的的接口(代理对象挂在那个接口下),第三个参数this代表当前HelloServiceProxy类,换句话说是使用HelloServiceProxy作为对象的代理。

invoke方法有三个参数:第一个proxy是代理对象,第二个是当前调用那个方法,第三个是方法的参数。

  1. public class ProxyTest {
  2. public static void main(String[] args) {
  3. HelloServiceProxy proxy = new HelloServiceProxy();
  4. HelloService service = new HelloServiceImpl();
  5. //绑定代理对象。
  6. service = (HelloService) proxy.bind(service, new Class[] {HelloService.class});
  7. //这里service经过绑定,就会进入invoke方法里面了。
  8. service.sayHello("张三");
  9. }
  10. }

测试结果:

  1. ############我是JDK动态代理################
  2. 我准备说hello
  3. hello 张三
  4. 我说过hello

源码跟踪

Proxy 类

  1. // 映射表:用于维护类装载器对象到其对应的代理类缓存
  2. private static Map loaderToCache = new WeakHashMap();
  3. // 标记:用于标记一个动态代理类正在被创建中
  4. private static Object pendingGenerationMarker = new Object();
  5. // 同步表:记录已经被创建的动态代理类类型,主要被方法 isProxyClass 进行相关的判断
  6. private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());
  7. // 关联的调用处理器引用
  8. protected InvocationHandler h;

Proxy 静态方法 newProxyInstance

  1. public static Object newProxyInstance(ClassLoader loader,
  2. Class<?>[] interfaces,
  3. InvocationHandler h)
  4. throws IllegalArgumentException {
  5. // 检查 h 不为空,否则抛异常
  6. if (h == null) {
  7. throw new NullPointerException();
  8. }
  9. // 获得与制定类装载器和一组接口相关的代理类类型对象
  10. /*
  11. * Look up or generate the designated proxy class.
  12. */
  13. Class<?> cl = getProxyClass0(loader, interfaces);
  14. // 通过反射获取构造函数对象并生成代理类实例
  15. /*
  16. * Invoke its constructor with the designated invocation handler.
  17. */
  18. try {
  19. final Constructor<?> cons = cl.getConstructor(constructorParams);
  20. final InvocationHandler ih = h;
  21. SecurityManager sm = System.getSecurityManager();
  22. if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
  23. // create proxy instance with doPrivilege as the proxy class may
  24. // implement non-public interfaces that requires a special permission
  25. return AccessController.doPrivileged(new PrivilegedAction<Object>() {
  26. public Object run() {
  27. return newInstance(cons, ih);
  28. }
  29. });
  30. } else {
  31. return newInstance(cons, ih);
  32. }
  33. } catch (NoSuchMethodException e) {
  34. throw new InternalError(e.toString());
  35. }
  36. }
  37. private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
  38. try {
  39. return cons.newInstance(new Object[] {h} );
  40. } catch (IllegalAccessException e) {
  41. throw new InternalError(e.toString());
  42. } catch (InstantiationException e) {
  43. throw new InternalError(e.toString());
  44. } catch (InvocationTargetException e) {
  45. Throwable t = e.getCause();
  46. if (t instanceof RuntimeException) {
  47. throw (RuntimeException) t;
  48. } else {
  49. throw new InternalError(t.toString());
  50. }
  51. }
  52. }

动态代理真正的关键是在 getProxyClass0 方法,

getProxyClass0方法分析

方法分为四个步骤:
1,对这组接口进行一定程度的安全检查
检查接口类对象是否对类装载器可见并且与类装载器所能识别的接口类对象是完全相同的,还会检查确保是 interface 类型而不是 class 类型。

这里写图片描述

2,从 loaderToCache 映射表中获取以类装载器对象为关键字所对应的缓存表,如果不存在就创建一个新的缓存表并更新到 loaderToCache。
loaderToCache存放键值对(接口名字列表,动态生成的代理类的类对象引用)。当代理类正在被创建时它会临时保存(接口名字列表,pendingGenerationMarker)。标记 pendingGenerationMarke 的作用是通知后续的同类请求(接口数组相同且组内接口排列顺序也相同)代理类正在被创建,请保持等待直至创建完成。

  1. /*
  2. * Find or create the proxy class cache for the class loader.
  3. */
  4. Map cache;
  5. synchronized (loaderToCache) {
  6. cache = (Map) loaderToCache.get(loader);
  7. if (cache == null) {
  8. cache = new HashMap();
  9. loaderToCache.put(loader, cache);
  10. }
  11. }
  12. 。。。。。
  13. do {
  14. // 以接口名字列表作为关键字获得对应 cache 值
  15. Object value = cache.get(key);
  16. if (value instanceof Reference) {
  17. proxyClass = (Class) ((Reference) value).get();
  18. }
  19. if (proxyClass != null) {
  20. // 如果已经创建,直接返回
  21. return proxyClass;
  22. } else if (value == pendingGenerationMarker) {
  23. // 代理类正在被创建,保持等待
  24. try {
  25. cache.wait();
  26. } catch (InterruptedException e) {
  27. }
  28. // 等待被唤醒,继续循环并通过二次检查以确保创建完成,否则重新等待
  29. continue;
  30. } else {
  31. // 标记代理类正在被创建
  32. cache.put(key, pendingGenerationMarker);
  33. // break 跳出循环已进入创建过程
  34. break;
  35. } while (true);

3,动态创建代理类的class对象

  1. /**
  2. * Choose a name for the proxy class to generate.
  3. */
  4. long num;
  5. synchronized (nextUniqueNumberLock) {
  6. num = nextUniqueNumber++;
  7. }
  8. String proxyName = proxyPkg + proxyClassNamePrefix + num;
  9. /*
  10. * Verify that the class loader hasn't already
  11. * defined a class with the chosen name.
  12. */
  13. // 动态地生成代理类的字节码数组
  14. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
  15. proxyName, interfaces);
  16. try {
  17. // 动态地定义新生成的代理类
  18. proxyClass = defineClass0(loader, proxyName,
  19. proxyClassFile, 0, proxyClassFile.length);
  20. } catch (ClassFormatError e) {
  21. /*
  22. * A ClassFormatError here means that (barring bugs in the
  23. * proxy class generation code) there was some other
  24. * invalid aspect of the arguments supplied to the proxy
  25. * class creation (such as virtual machine limitations
  26. * exceeded).
  27. */
  28. throw new IllegalArgumentException(e.toString());
  29. }
  30. // 把生成的代理类的类对象记录进 proxyClasses 表
  31. proxyClasses.put(proxyClass, null);

首先根据规则(接口public与否),生成代理类的名称,$ProxyN格式,然后动态生成代理类。
所有的代码生成的工作都由 ProxyGenerator 所完成了,该类在rt.jar中,需要反编译

  1. public static byte[] generateProxyClass(final String name,
  2. Class[] interfaces)
  3. {
  4. ProxyGenerator gen = new ProxyGenerator(name, interfaces);
  5. // 这里动态生成代理类的字节码,由于比较复杂就不进去看了
  6. final byte[] classFile = gen.generateClassFile();
  7. // 如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上
  8. if (saveGeneratedFiles) {
  9. java.security.AccessController.doPrivileged(
  10. new java.security.PrivilegedAction<Void>() {
  11. public Void run() {
  12. try {
  13. FileOutputStream file =
  14. new FileOutputStream(dotToSlash(name) + ".class");
  15. file.write(classFile);
  16. file.close();
  17. return null;
  18. } catch (IOException e) {
  19. throw new InternalError(
  20. "I/O exception saving generated file: " + e);
  21. }
  22. }
  23. });
  24. }
  25. // 返回代理类的字节码
  26. return classFile;
  27. }

4,代码生成过程进入结尾部分,根据结果更新缓存表,如果成功则将代理类的类对象引用更新进缓存表,否则清楚缓存表中对应关键值,最后唤醒所有可能的正在等待的线程。

  1. finally {
  2. /*
  3. * We must clean up the "pending generation" state of the proxy
  4. * class cache entry somehow. If a proxy class was successfully
  5. * generated, store it in the cache (with a weak reference);
  6. * otherwise, remove the reserved entry. In all cases, notify
  7. * all waiters on reserved entries in this cache.
  8. */
  9. synchronized (cache) {
  10. if (proxyClass != null) {
  11. cache.put(key, new WeakReference(proxyClass));
  12. } else {
  13. cache.remove(key);
  14. }
  15. cache.notifyAll();
  16. }
  17. }
  18. return proxyClass;

InvocationHandler解析

代码参考: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

发表评论

表情:
评论列表 (有 0 条评论,394人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java 动态代理实现

    Java动态代理只能针对接口进行动态代理。如果需要对类进行实现代理可以使用:CGLIB,ASM等相关的操作字节码实现(在这里先只介绍下SUN 基于接口动态代理的实现)。 代码

    相关 java动态代理原理结构

    代理:设计模式 代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被

    相关 java动态代理原理

    代理:设计模式 代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被