如何以编程方式通过引导类加载器加载另一个类?

Lat*_*ter 3 java classloader bootclasspath

我知道有一个“引导类加载器”从 jre/lib(rt.jar 等)加载所有类。是否有可能让我使用这个“引导类加载器”以便从另一个非 java 包加载额外的类?

我说的是引导程序类路径,它与此答案中描述的常规类路径非常不同:我应该如何在运行时动态加载 Jars?

Hol*_*ger 7

引导类加载器表示为null,例如,在调用时Class.getClassLoader()。这可以直接提供给Class.forName(String,boolean,ClassLoader)

如果参数loader为空,则通过引导类加载器加载该类。

因此,您可以尝试通过引导加载程序显式加载一个类,使用Class.forName(className,true,null)或解析相对于另一个类的上下文的类,Class.forName(className,true,context.getClassLoader())而无需对引导加载程序进行特殊处理。


如果你想定义一个运行时类,就像由引导加载程序加载一样,没有标准的解决方案。有一种方法sun.misc.Unsafe.defineClass(String, byte, int, int, ClassLoader, ProtectionDomain)允许在特定的类加载器中定义一个类,这是一个专有的 API,将来可能会消失。

从 Java 9 开始,有java.lang.invoke.MethodHandles.Lookup.defineClass(byte[])一个标准方法在另一个类的上下文中定义一个类,但它需要非标准方式,例如具有访问覆盖的反射以获取Lookup具有引导加载类的适当访问权限的对象,例如java.lang.Object. 已经有关于Lookup为其他目的创建这样一个对象的问答,例如这个。但是,不能保证此类 Reflection hack 在未来版本中有效。

对于 Java 代理,如果不介意这涉及 I/O ,还可以选择将类文件转储到临时 Jar 文件中并编程方式将其添加到引导类路径

还有一个要注意的问题。当您希望其他引导加载的类找到您的类时,您必须在第一次尝试访问该类之前添加它,因为 JVM 的解析器会记住结果,即使它会因错误而失败。

  • @LatencyFighter 关于系统类加载器的另一个问题很容易回答,因为那时存在一个现有的 `ClassLoader` 实例,而不是由 `null` 表示的引导类加载器。诚然,我有点惊讶 Java 代理没有这样的选项来简单地定义一个类,但是,我扩展了我的答案并列出了我目前知道的所有选项。 (2认同)
  • @FrancescoMenzani `new MyClass()` 将询问应用程序类加载器,应用程序类加载器将首先询问父加载器,因此如果该类位于引导路径上,它将由引导加载器加载。首先反思性地加载它不会影响该逻辑。操作引导类加载路径的选项[已记录](https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html#BABHDABI)... (2认同)