ModuleLayer和ClassLoader之间是什么关系?

问题描述 投票:2回答:2

让我们考虑以下情况。一共有三层。

BootLayer (moduleA)
     |
     |_______Child1(moduleB)
                |
                |__________ Child2(moduleC)

[Child1是BootLayer的孩子,Child2是Child1的孩子。

[Child1Child2由相同的代码创建:

ClassLoader parentClassLoader = ClassLoader.getSystemClassLoader();
ModuleLayer layer = parentLayer.defineModulesWithOneLoader(cf, parentClassLoader);

您看到两个子层的父类加载器是SystemClassLoader。但是,moduleB可以使用moduleA的类别,但是moduleC可以使用moduleAmoduleB的类别。

我是类加载的初学者,但是我已经读过parent-first delegation model。但是,如果两个子层都使用SystemClassLoader,那么为什么他们看到其他层的类呢?有人可以解释吗?

java classloader java-module
2个回答
1
投票

首先,我假设Child2的父层是Child1。


让我们从specification开始:

模块成员资格是根据运行时程序包(§5.3)定义的。程序确定每个模块中软件包的名称,以及将创建命名软件包的类和接口的类加载器。然后,它指定包和类加载器以调用类ModuleLayer的defineModules方法。调用defineModules会使Java虚拟机创建与类加载器的运行时程序包相关联的新运行时模块。

这里重要的部分是:定义模块层时,必须知道每个模块的软件包名称。

运行时模块通过defineModules的语义隐式地恰好是一层的一部分。但是,类加载器可以在不同层的运行时模块中创建类,因为可以为defineModules的多个调用指定相同的类加载器。访问控制由类的运行时模块控制,而不是由创建该类的类加载器或由该类加载器服务的层控制。

类加载器委派无关。

类加载器和层之间存在异同。一方面,一层类似于类加载器,因为每个层可以分别委派给一个或更多个父层或类加载器,这些父层或类加载器分别在较早的时间创建了模块或类。 即,指定给一个层的模块集合可能取决于未指定给该层的模块,而是先前指定给一个或多个父层的模块。另一方面,可以使用一个层来创建新模块类加载器只能使用一次,而在任何时候都可以通过defineClass方法的多次调用来创建新的类或接口。

(重点是我的)


这应该足以回答您的问题:

  • 因为Child1的父层是引导层,所以Child1中的模块可以依赖于引导层中的模块。
  • 因为Child2的父层是Child1,所以Child2层中的模块可以取决于Child1和引导层中的模块。

0
投票

答案是艾伦·贝特曼(Alan Bateman)在拼图开发邮件列表中发布的,并在这里发布。

模块层是高级主题。 ClassLoader也是一个高级主题。当使用模块层并使用defineModulesWithXXX方法创建模块层时,您通常不需要太在意类加载器。它们仍然用于加载类,但是它们大部分在后台(而不是您的脸)。

您也不必太担心为defineOneWithOneLoader方法指定的“父类加载器”。从模块加载类时不使用它,仅在moduleBmoduleC中的代码试图加载不在模块中的类的情况下才使用,例如Class.forName("Foo")其中Foo在类路径上。因此,可能最好在开始时忽略父类加载器。

API docs解释了委派如何与模块一起使用,但是对于此处所需的内容可能还不够清楚。在您的示例中,支持L1是子层1中moduleB的类加载器,L2是子层2中moduleC的类加载器。进一步假设模块声明为:

module moduleC {
     requires moduleB;
}

module moduleB {
     exports b;
}

Child1的配置非常简单:一个moduleB读为java.base

Child2的配置也非常简单:一个moduleC读取moduleBjava.base

创建Child1时,将创建L1并将moduleB映射到L1。当moduleB中的代码尝试解析其自身模块中对类的引用时,它将由L1(无委托)加载。当moduleB引用java.base中的类时,则L1将委派给boot loader

创建Child2时,将创建L2并将moduleC映射到L2。当moduleC中的代码尝试解析其自身模块中对类的引用时,它将由L2(无委托)加载。当moduleC引用b.*类时,它将被委派给L1来解析引用。当moduleC引用java.base中的类时,L2将委托给引导加载程序。

如果将其绘制出来,则应该看到类加载器委托是“直接委托”,并且完全反映了可读性图(配置对象)中的边缘。

希望这足以开始您的工作。它确实需要图表来解释其中的一些细节。如我所说,在使用模块层时,您几乎可以忽略类加载器的详细信息。

© www.soinside.com 2019 - 2024. All rights reserved.