模块 java.base

包 java.lang.module


java.lang.module
通过解析和服务绑定支持模块描述符和创建模块配置的类。

除非另有说明,否则将 null 参数传递给此包中任何类或接口的构造函数或方法将导致抛出 NullPointerException 。此外,除非另有说明,否则使用包含 null 元素的数组或集合调用方法将导致 NullPointerException

模块解析

解析是计算模块如何相互依赖的过程。该过程发生在编译时和运行时。

决议是一个两步过程。第一步递归地枚举一组根模块的“requires”指令。如果所有枚举的模块都是可观察的,那么第二步计算它们的可读性图。可读性图体现了模块如何相互依赖,进而控制跨模块边界的访问。

第一步:递归枚举

递归枚举采用一组模块名称,查找它们的每个模块声明,并针对每个模块声明递归枚举:

  • 由带有“transitive”修饰符的“requires”指令给出的模块名称,以及

  • 由主机系统自行决定,由不带“transitive”修饰符的“requires”指令给出的模块名称。

在一组可观察模块中查找模块声明。可观察模块集以特定于实现的方式确定。可观察模块集可能包括具有显式声明的模块(即具有 module-info.java 源文件或 module-info.class 文件)和具有隐式声明的模块(即 自动模块 )。因为自动模块没有显式模块声明,所以它没有自己的“requires”指令,尽管它的名称可能由显式模块声明的“requires”指令给出。

以特定于实现的方式确定根模块集,其名称是该算法的初始输入。该组根模块可以包括自动模块。

如果此算法至少枚举了一个自动模块,则必须枚举每个可观察的自动模块,无论它们的任何名称是否由显式模块声明的“requires”指令给出。

如果出现以下任何一种情况,则解析失败:

  • 任何根模块都是不可观察的。

  • 任何名称由带有“transitive”修饰符的“requires”指令给出的模块都是不可观察的。

  • 根据主机系统的判断,任何模块的名称由没有“transitive”修饰符的“requires”指令给出是不可观察的。

  • 此步骤中的算法将相同的模块名称枚举两次。这表示“需要”指令中的循环,忽略任何“传递”修饰符。

否则,决议继续进行到步骤 2。

第 2 步:计算可读性图

“requires”指令(与“transitive”无关)表示一个模块依赖于其他模块。 'transitive' 修饰符的作用是使其他模块也依赖于另一个模块。如果模块 M“需要传递 N”,那么不仅 M 依赖于 N,任何依赖于 M 的模块也依赖于 N。模块 N 而不会破坏具有“需要 M”指令的模块。

模块依赖关系由可读性图表示。可读性图是一个有向图,其顶点是步骤 1 中枚举的模块,其边表示模块对之间的可读性。边缘指定如下:

首先,可读性由枚举模块的“requires”指令决定,忽略任何“传递”修饰符:

  • 对于每个“需要”B 的枚举模块 A:A“读取”B。

  • 对于每个自动枚举模块 X:X“读取”所有其他枚举模块(“好像”一个自动模块对每个其他枚举模块都有“requires”指令)。

其次,增加了可读性以说明“传递”修饰符:

  • 对于每个“读取”B 的枚举模块 A:

    • 如果 B“需要传递”C,则 A“读取”C 以及 B。此扩充是递归的:因为 A“读取”C,如果 C“需要传递”D,则 A“读取”D 以及 C,并且B.

    • 如果 B 是自动模块,则 A 会“读取”所有其他枚举的自动模块。 (“好像”一个自动模块对每个其他枚举的自动模块都有“需要传递”指令)。

最后,每个模块“读取”自己。

如果可读性图中出现以下任何一种情况,则解析失败:

  • 一个模块“读取”两个或多个具有相同名称的模块。这包括模块“读取”另一个与自身同名的情况。

  • 两个或多个模块将具有相同名称的包导出到“读取”两者的模块。这包括包含包 p 的模块 M“读取”另一个将 p 导出到 M 的模块的情况。

  • 模块 M 声明它“使用 pS”或“为 pS 提供...”,但包 p 既不在模块 M 中,也不由 M“读取”的任何模块导出到 M。

否则解析成功,解析结果为可读图。

根模块

编译时的根模块集通常是正在编译的模块集。在运行时,根模块集通常是指定给“java”启动器的应用程序模块。在未命名模块中编译代码时,或者在运行时从类路径加载主应用程序类时,默认的根模块集是特定于实现的。在 JDK 中,默认的根模块集包含在升级模块路径或系统模块中可观察到的每个模块,并且至少导出一个没有限定的包。

可观察模块

编译时和运行时的可观察模块集是通过搜索几个不同的路径以及通过搜索环境中内置的已编译模块来确定的。搜索顺序如下:

  1. 仅在编译时,编译模块路径。此路径包含源代码形式的模块定义。

  2. 升级模块路径。此路径包含模块的编译定义,这些定义将优先于任何模块的编译定义可升级模块存在于 (3) 和 (4) 中。请参阅 Java SE 平台以了解哪些标准模块是可升级的。

  3. 系统模块,它们是环境中内置的已编译定义。

  4. 应用程序模块路径。此路径包含库和应用程序模块的已编译定义。

“requires”带有“static”修饰符的指令

具有“static”修饰符的“requires”指令在运行时表示可选的依赖性。如果模块声明它“requires static M'”,则解析不会搜索可观察模块以寻找 M 来满足依赖性。但是,如果 M 在步骤 1 中被递归枚举,那么所有被枚举的模块和“需要静态 M”都将读取 M。

完整性

在编译时解析可能是部分的,因为编译一组模块可能不需要完整的传递闭包。至少,在编译时构造和验证的可读性图包括正在编译的模块、它们的直接依赖关系以及所有隐式声明的依赖关系(需要传递)。

在运行时,解析是一个附加过程。步骤 1 中的递归枚举可能与先前的解析相关,因此根模块或在“requires”指令中命名的模块在先前(或父级)解析中枚举时不会被枚举。因此,作为解析结果的可读性图可能具有在步骤 1 中枚举的模块的顶点,但具有表示该模块读取由先前(或父级)解析枚举的模块的边。

自从:
9