让我们考虑以下情况。一共有三层。
BootLayer (moduleA)
|
|_______Child1(moduleB)
|
|__________ Child2(moduleC)
[Child1是BootLayer的孩子,Child2是Child1的孩子。
[Child1
和Child2
由相同的代码创建:
ClassLoader parentClassLoader = ClassLoader.getSystemClassLoader();
ModuleLayer layer = parentLayer.defineModulesWithOneLoader(cf, parentClassLoader);
您看到两个子层的父类加载器是SystemClassLoader。但是,moduleB
可以使用moduleA
的类别,但是moduleC
可以使用moduleA
和moduleB
的类别。
我是类加载的初学者,但是我已经读过parent-first delegation model
。但是,如果两个子层都使用SystemClassLoader,那么为什么他们看到其他层的类呢?有人可以解释吗?
首先,我假设Child2的父层是Child1。
让我们从specification开始:
模块成员资格是根据运行时程序包(§5.3)定义的。程序确定每个模块中软件包的名称,以及将创建命名软件包的类和接口的类加载器。然后,它指定包和类加载器以调用类ModuleLayer的defineModules方法。调用defineModules会使Java虚拟机创建与类加载器的运行时程序包相关联的新运行时模块。
这里重要的部分是:定义模块层时,必须知道每个模块的软件包名称。
运行时模块通过defineModules的语义隐式地恰好是一层的一部分。但是,类加载器可以在不同层的运行时模块中创建类,因为可以为defineModules的多个调用指定相同的类加载器。访问控制由类的运行时模块控制,而不是由创建该类的类加载器或由该类加载器服务的层控制。
类加载器委派无关。
类加载器和层之间存在异同。一方面,一层类似于类加载器,因为每个层可以分别委派给一个或更多个父层或类加载器,这些父层或类加载器分别在较早的时间创建了模块或类。 即,指定给一个层的模块集合可能取决于未指定给该层的模块,而是先前指定给一个或多个父层的模块。另一方面,可以使用一个层来创建新模块类加载器只能使用一次,而在任何时候都可以通过defineClass方法的多次调用来创建新的类或接口。
(重点是我的)
这应该足以回答您的问题:
答案是艾伦·贝特曼(Alan Bateman)在拼图开发邮件列表中发布的,并在这里发布。
模块层是高级主题。 ClassLoader也是一个高级主题。当使用模块层并使用defineModulesWithXXX
方法创建模块层时,您通常不需要太在意类加载器。它们仍然用于加载类,但是它们大部分在后台(而不是您的脸)。
您也不必太担心为defineOneWithOneLoader
方法指定的“父类加载器”。从模块加载类时不使用它,仅在moduleB
或moduleC
中的代码试图加载不在模块中的类的情况下才使用,例如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
读取moduleB
和java.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
将委托给引导加载程序。
如果将其绘制出来,则应该看到类加载器委托是“直接委托”,并且完全反映了可读性图(配置对象)中的边缘。
希望这足以开始您的工作。它确实需要图表来解释其中的一些细节。如我所说,在使用模块层时,您几乎可以忽略类加载器的详细信息。