What is the relation between ModuleLayer and ClassLoader?

亡梦爱人 提交于 2021-01-03 08:20:21

问题


Let's consider the following situation. There are three layers.

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

Child1 is the child of the BootLayer, Child2 is the child of the Child1.

Child1 and Child2 was created by the same code:

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

As you see the parent class loader of both child layers is SystemClassLoader. However, moduleB can use classes of moduleA, but moduleC can use classes of moduleA and moduleB.

I am a beginner at classloading, but I've read about parent-first delegation model. However, if for both child layers SystemClassLoader is used, then why they see classes from other layers? Could anyone explain?


回答1:


The answer was given by Alan Bateman in jigsaw-dev mailing list and is posted here.

Module layers are an advanced topic. ClassLoaders are also an advanced topic. When working with module layers and using the defineModulesWithXXX methods to create the module layers, then you mostly don't need to be too concerned with class loaders. They are still used to load classes but they are mostly in the background (and not in your face).

You also don't need to be too concerned with the "parent class loader" that you specify to the defineOneWithOneLoader method. It's not used when loading classes from modules, it's only when for cases where the code in moduleB or moduleC tries to load a class that is not in a module, maybe Class.forName("Foo") where Foo is on the class path. So probably best to ignore the parent class loader when starting out.

The API docs explain how the delegation works with modules but maybe it's not clear enough for what is needed here. In your example, support L1 is the class loader for moduleB in child layer 1, and L2 is the class loader for moduleC in child layer 2. Further suppose the module declarations are:

module moduleC {
     requires moduleB;
}

module moduleB {
     exports b;
}

The configuration for Child1 is very simple: one moduleB that reads java.base

The configuration for Child2 is also very simple: one moduleC that reads moduleB and java.base.

When Child1 is created it will create L1 and map moduleB to L1. When code in moduleB tries to resolve a reference to a class in its own module then it will be loaded by L1 (no delegation). When moduleB references a class in java.base then L1 will delegate to the boot loader.

When Child2 is created it will create L2 and map moduleC to L2. When code in moduleC tries to resolve a reference to a class in its own module then it will be loaded by L2 (no delegation). When moduleC references a b.* class then it will be delegate to L1 to resolve the reference. When moduleC references a class in java.base then L2 will delegate to the boot loader.

If you draw this out then you should see that the class loader delegation is "direct delegation" and exactly mirrors the edges in the readability graph (Configuration object).

Hopefully this is enough to get your started. It really needs diagrams and graphs to explain some of these details probably. As I said, you can mostly ignore class loader details when working with module layers.




回答2:


First, I assume the parent layer of Child2 is Child1.


Let's start with the specification:

Module membership is defined in terms of run-time packages (§5.3). A program determines the names of the packages in each module, and the class loaders that will create the classes and interfaces of the named packages; it then specifies the packages and class loaders to an invocation of the defineModules method of the class ModuleLayer. Invoking defineModules causes the Java Virtual Machine to create new run-time modules that are associated with the run-time packages of the class loaders.

The important part here is: When a module layer is defined, the package names for each module have to be known.

A run-time module is implicitly part of exactly one layer, by the semantics of defineModules. However, a class loader may create classes in the run-time modules of different layers, because the same class loader may be specified to multiple invocations of defineModules. Access control is governed by a class's run-time module, not by the class loader which created the class or by the layer(s) which the class loader serves.

Class loader delegation is irrelevant.

There are similarities and differences between class loaders and layers. On the one hand, a layer is similar to a class loader in that each may delegate to, respectively, one or more parent layers or class loaders that created, respectively, modules or classes at an earlier time. That is, the set of modules specified to a layer may depend on modules not specified to the layer, and instead specified previously to one or more parent layers. On the other hand, a layer may be used to create new modules only once, whereas a class loader may be used to create new classes or interfaces at any time via multiple invocations of the defineClass method.

(emphasis mine)


This should be enough to answer your question:

  • Because the parent layer of Child1 is the boot layer, modules in Child1 can depend on modules in the boot layer.
  • Because the parent layer of Child2 is Child1, modules in layer Child2 can depend on modules in Child1 and the boot layer.


来源:https://stackoverflow.com/questions/61195909/what-is-the-relation-between-modulelayer-and-classloader

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!