如何在 Java 中加载修改后的超类?

How to load a modified superclass in Java?

我做错了什么? 如果您需要更多信息,我的 GitHub.

上有详细的主题

Java classloader 首先 在查看子项之前先搜索父 classloader。

The loadClass method in ClassLoader performs these tasks, in order, when called to load a class:

  1. If a class has already been loaded, it returns it.
  2. Otherwise, it delegates the search for the new class to the parent class loader.
  3. If the parent class loader does not find the class, loadClass calls the method findClass to find and load the class.

(Understanding Extension Class Loading - Oracle)

如果您想更改该顺序,您还需要重写 loadClass 方法,但有很多注意事项,除非您非常了解 class 加载,否则不建议这样做。

  • 更简单的选择是确保父 class 加载程序找不到原始 class B.

有几件事要知道:

  • 对于大多数事情,处理线程的上下文 class 加载程序已经过时,因为它没有任何影响。它更像是一个约定;如果有其他代码查询和使用它,设置它会产生影响。对于标准的class加载过程,没有任何意义。不幸的是,文档没有提到这一点,并让它看起来像一个相关的东西。或许,当初加入的时候,是为了有更多的意义吧。
  • 作为 ,当通过您的自定义加载器加载 A 时,它将委托给其父加载器,返回由父加载的 class A
  • resolving class 引用时,JVM 将始终使用referrer 的defining 加载器。因此,当从 AB 的引用被解析时,JVM 将始终使用定义 class A
  • 的父加载器

最后一点意味着,即使您修改自定义 class 加载程序以首先查找自己的 classes 而不是遵循首先查询父级的标准模型,它也不会解决问题,如果它没有自己的 A,那么,它仍然 returns 父的 A,其引用将使用父解决。由于您在请求 A 之前调用 defineClass,因此查找顺序根本无关紧要,因为您的自定义加载程序已经定义了 B,如果有人请求它,它就会返回B

所以你可以让你的自定义加载器也加载和定义 A。或者,在系统 ClassLoader 加载 B 之前,您使用反射覆盖对 defineClass 的访问权限。最干净的解决方案是将 class 修改逻辑实现为 Java 代理,它可以使用 Instrumentation API 在加载时立即拦截和更改 B 的定义。