什么是自动模块?

Zhe*_*lov 23 java java-platform-module-system java-9 java-module

在stackoverflow上多次提到自动模块,但我找不到自动模块的完整,简洁和自给自足的定义.

那么,什么是自动模块?它是否导出所有包裹?它打开所有包裹吗?它是否读取所有其他模块?

Nic*_*lai 31

我首先回答你的实际问题("什么是自动模块?"),但我也解释了它们的用途.很难理解为什么自动模块在没有这些信息的情况下表现得如此.

什么是自动模块?

模块系统从它在模块路径上找到的每个JAR创建一个模块.对于模块化JAR(即具有模块描述符的JAR),它们可以直接定义模块的属性(名称,需求,导出).对于普通JAR(没有模块描述符),这种方法不起作用,那么模块系统应该做什么呢?它会自动创建一个模块 - 可以说是一个自动模块 - 并对三个属性进行最安全的猜测.

名称

获取名称有两个步骤:

  • 如果JAR Automatic-Module-Name在其清单中定义了标头,则它定义模块的名称
  • 否则,JAR文件名用于确定名称

第二种方法本质上是不稳定的,因此不应发布依赖于这种自动模块的模块.Maven警告说.

需要

由于普通JAR表示没有require子句,因此模块系统允许自动模块读取所有其他模块,使其成为可读性图(也就是模块图).与显式模块不同,自动模块也读取未命名的模块,该模块包含从类路径加载的所有内容.这个看似微不足道的细节变得非常重要(见下文).

自动模块有一些进一步的可读性怪癖,但是:

  • 第一个自动模块解决后,所有其他模块也一样.这意味着一旦模块路径上的单个普通JAR被另一个模块引用,所有普通JAR都将作为自动模块加载.
  • 自动模块意味着所有其他自动模块的可读性,这意味着读取其中一个模块的模块会读取所有模块.

总而言之,这可能会产生令人遗憾的影响,即依赖于几个普通JAR的显式模块(即非自动模块)只需要其中一个就可以逃脱(只要其他模块路径上也是如此) .

出口/开幕

由于JAR不包含哪些包被视为公共API而哪些包不是公共API的信息,因此模块系统会导出所有包并打开它们以进行深入反射.

更多

模块系统还扫描META-INF/services并使自动模块提供其中命名的服务.假设允许自动模块使用所有服务.

最后,Main-Class清单条目也被处理,因此定义一个的普通JAR可以像自动模块一样启动,其中主类是使用jar工具设置的(即java --module-path my-app.jar --module my.app).

适当的模块

创建自动模块后,它将被视为任何其他模块.这明确地包括模块系统将其检查为任何其他模块,例如用于拆分包.

什么是自动模块

引入模块的原因之一是使编译和启动应用程序更加可靠,并且可以更快地发现类路径可能出现的错误.requires条款的一个关键方面是条款.

为了保证它们的可靠性,模块声明无法使用除命名模块之外的任何其他模块,这会排除从类路径加载的所有内容.如果故事在这里结束,模块化JAR只能依赖于其他模块化JAR,这将迫使生态系统从下往上进行模块化.

然而,这是不可接受的,因此引入自动模块作为模块化JAR依赖于非模块化JAR的手段,您需要做的就是将普通JAR放在模块路径上并通过模块系统的名称来要求它给它.

有趣的是,因为自动模块读取未命名的模块,所以可行(我通常建议这样做)将其依赖项留在类路径上.这样,自动模块充当从模块到类路径的桥梁.

您的模块可以位于一侧,需要它们作为自动模块的直接依赖关系,并且间接依赖关系可以保留在另一侧.每当您的一个依赖项变成一个显式模块时,它就会在模块化的一侧离开桥,并将其直接依赖关系作为自动模块绘制到桥上.

  • 你知道自动模块是否禁止分割包吗?我认为这个答案值得一提。 (2认同)
  • 将其包含在 _Proper module_ 下。 (2认同)
  • <div class="Text"/> (2认同)

Nam*_*man 6

一个自动模块是隐式定义的,因为它命名模块没有一个模块声明。相比之下,普通命名模块是显式定义的,带有模块声明;以后我们将这些称为显式模块

使用这些的主要好处是它们允许您在编译或运行时将工件视为模块,而无需等待它迁移到模块化结构。

如果在其主清单条目中具有属性Automatic-Module-Name,则自动模块的模块名称源自用于包含工件的 JAR 文件。模块名称由 .jar 文件的名称派生而来ModuleFinder


由于自动模块没有模块声明,因此实际上无法告诉它读取、打开或导出所有模块或包的内容。

? 因此,由于没有显式导出/打开驻留在自动模块中的包,描述为 -

.. 没有实用的方法来判断自动模块中的哪些包是供其他模块使用的,还是供仍在类路径上的类使用的。因此,自动模块中的每个包都被认为是导出的,即使它实际上可能仅供内部使用。

? 进一步引用链接 -

...没有实用的方法可以提前告诉自动模块可能依赖哪些其他模块。因此,解析模块图后,自动模块会读取所有其他命名模块,无论是自动的还是显式的。

的一个建议-自动模块提供封装的传统水平:所有的包都打开深反射通路出口对于普通编译时和运行时访问他们的公共类型


? 此外,一个自动模块

授予所有其他自动模块的隐含可读性

由于这个原因,当在一个模块中使用多个自动模块时,它的

..无法确定自动模块(xyz)中的一个导出包是否包含一种类型,该类型的签名指代某个其他自动模块(abc)中定义的类型。


man*_*uti 6

(对于缺少完整定义,您似乎是正确的,因为我没有在语言或 JVM 规范中找到它)

Javadoc 在包类中提供了大量的正式定义java.lang.module

部分引用自https://docs.oracle.com/javase/9 ​​/docs/api/java/lang/module/ModuleFinder.html#automatic-modules :

module-info.class顶级目录中没有 的 JAR 文件定义了一个自动模块,如下所示:

  • 如果 JAR 文件在其主清单中具有属性“ Automatic-Module-Name”,则其值是模块名称。模块名称是从 JAR 文件的名称派生的。

...

并来自https://docs.oracle.com/javase/9​​/docs/api/java/lang/module/ModuleDescriptor.html

自动模块的模块描述符不声明任何依赖关系(对 的强制依赖除外java.base),并且不声明任何导出或打开的包。自动模块在解析期间接受特殊处理,以便它们读取配置中的所有其他模块。当一个自动模块在 Java 虚拟机中实例化时,它会读取每个未命名的模块,并被视为所有包都已导出并打开。

编辑(2021):

Java 语言规范中有关模块依赖性的部分有以下有趣的注释:

Java SE 平台区分显式声明的命名模块(即使用模块声明)和隐式声明的命名模块(即自动模块)。然而,Java 编程语言并没有表现出这种区别:requires 指令引用命名模块,而不考虑它们是显式声明还是隐式声明。

虽然自动模块便于迁移,但它们并不可靠,因为当作者将它们转换为显式声明的模块时,它们的名称和导出的包可能会发生变化。如果 require 指令引用自动模块,则鼓励 Java 编译器发出警告。如果指令中出现传递修饰符,建议特别强烈警告。