9

动机

假设我们有一个如下所示的类加载层次结构:

 Bootstrap
     |
  System
     |
  Custom

假设CustomClassloader 用于加载一个类com.example.SomeClass。它检查System类加载器是否可以加载它,然后再次检查类加载器是否Bootstrap可以加载它。由于两者都不能,com.example.SomeClassCustom类加载器加载。

任何com.example.SomeClass依赖的类都一样。我相信我理解这个过程。

问题

我不明白为什么Custom要首先尝试加载com.example.SomeClass。在 Java 应用程序中如何选择当前的类加载器?

4

2 回答 2

15

小介绍

如您所知,默认情况下 Java 使用引导类加载器和系统类加载器。第一个负责加载引导类(其类路径包含诸如 rt.jar 之类的工件),第二个负责保存应用程序的类路径。通常,在您的环境变量中定义或在 JVM 中给出的类路径开始使用该-cp参数。

答案

com.example.SomeClass仅当发生以下两种情况之一时,您的自定义类加载器才会加载该类Custom:要么在启动时定义自定义类加载器用作系统类加载器,要么在运行时通过它显式加载类。

关于每个选项的更多信息:

  • 在应用程序启动时:您可以在启动 JVM 实例时定义,而不是使用您自己的 Java 的默认系统类加载器。为此,只需调用 java 并定义以下环境变量:

    -Djava.system.class.loader=my.tests.classloaders.Custom
    

    在这种情况下,该 JVM 实例中的应用程序中的所有类实际上都将由Custom类加载器加载。

  • 在运行时:您可以在运行时使用您的自定义类加载器加载一个类。这是通过创建自定义类加载器的实例并从中加载类来实现的

         ClassLoader classloader = new CustomClassLoader();
         Class someClass = classloader.loadClass("com.example.SomeClass");
    

就像@Noofiz 在他的回答中所说的那样,一旦您加载了一个类,所有需要但尚未加载的引用类都将通过关联的类加载器加载。因此,如果您使用自定义类加载器加载一个类,所有引用的类也将通过它加载。加载所有类时,您可以做任何您想做的事情,记录正在加载的类,委托给父类加载器,自己加载类...

一些额外的信息

通常,实现自定义类加载器的最佳方法是使用您提到的委托模型。这是因为一个类实际上不仅由类的字节码定义,还由其类加载器定义,这意味着由两个不同的类加载器加载的类不会是同一个类

这意味着当您的自定义类加载器委托给它的父类时,您要确保该类可用于更广泛的范围。大多数情况下,这将是您想要的,但并非总是如此。

如果出于某种原因您想要类隔离,那么您的自定义类加载器可能会以相反的方式实现。首先,它会尝试自行加载该类,并且只有在它没有找到该类(或者是 JVM 系统类或您可能想要跳过的任何其他类)时才将其委托给它的父类。例如,Web 应用程序容器以这种方式工作,允许上下文重新部署(基本上它们丢弃类加载器并创建一个新的加载器再次加载所有内容)和 Web 应用程序之间的完全类隔离。

正如我已经说过的,处理类加载一点也不简单,要么你真的知道你在做什么,要么你肯定会发现自己陷入了一些奇怪的巫毒问题。

也许已经离题太远了,但如果你想更多地了解类加载器和隔离,你可以查看一个名为classworlds的旧开源项目。即使这个项目很老,我还是建议它,因为它是一个小项目,充满了关于类加载机制的知识,你可以轻松地深入了解。

于 2013-03-06T21:22:27.970 回答
2

第一次在某个方法中请求每个类,每个方法都是某个类的一部分,该类已经加载并定义了它的类加载器。因此,当需要一个新类时,它会通过当前方法的类的类加载器进行查找。如果一个类是通过自定义类加载器加载的,它将成为该类的方法加载的所有类的基类加载器。JVM 规范没有定义如何静态解析类(在启动时加载所有图形)或动态解析(第一次请求时)。但是静态加载会花费太长时间,因此它没有被使用,并且当应用程序已经运行时我们会收到 ClassNotFoundError。类和接口解析

于 2013-03-06T15:09:20.257 回答