什么是自动模块?

What is an automatic module?

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

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

An automatic module is a named module that is defined implicitly, since it does not have a module declaration. An ordinary named module, by contrast, is defined explicitly, with a module declaration; we will henceforth refer to those as explicit modules.

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

如果自动模块的主清单条目中具有 Automatic-Module-Name 属性,则自动模块的模块名称源自用于包含工件的 JAR 文件。模块名称是通过 ModuleFinder.

从 JAR 文件的名称派生的。

由于自动模块没有模块声明这一事实,实际上无法判断所有模块或包读取、打开或导出的内容。

➜ 因此,由于自动模块中的包没有明确的 exports/opens,描述为 -

.. no practical way to tell which of the packages in an automatic module are intended for use by other modules, or by classes still on the class path. Every package in an automatic module is, therefore, considered to be exported even if it might actually be intended only for internal use.

➜ 进一步引用 link -

... no practical way to tell, in advance, which other modules an automatic module might depend upon. After a module graph is resolved, therefore, an automatic module is made to read every other named module, whether automatic or explicit.

proposals 之一 - 自动模块提供了传统级别的封装:所有包都开放深度反射访问导出用于普通编译时和 运行 时访问 到它们的 public 类型


➜ 此外,一个自动模块

grants implied readability to all other automatic modules

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

..not feasible to determine whether one of the exported packages in an automatic module(x.y.z) contains a type whose signature refers to a type defined in some other automatic module(a.b.c).

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

Java文档在 java.lang.module 包 类.

中提供了大量正式定义

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

A JAR file that does not have a module-info.class in its top-level directory defines an automatic module, as follows:

  • If the JAR file has the attribute "Automatic-Module-Name" in its main manifest then its value is the module name. The module name is otherwise derived from the name of the JAR file.

...

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

The module descriptor for an automatic module does not declare any dependences (except for the mandatory dependency on java.base), and does not declare any exported or open packages. Automatic module receive special treatment during resolution so that they read all other modules in the configuration. When an automatic module is instantiated in the Java virtual machine then it reads every unnamed module and is treated as if all packages are exported and open.

编辑 (2021):

来自 Java 语言规范的 section on module dependences 有以下有趣的注释:

The Java SE Platform distinguishes between named modules that are explicitly declared (that is, with a module declaration) and named modules that are implicitly declared (that is, automatic modules). However, the Java programming language does not surface the distinction: requires directives refer to named modules without regard for whether they are explicitly declared or implicitly declared.

While automatic modules are convenient for migration, they are unreliable in the sense that their names and exported packages may change when their authors convert them to explicitly declared modules. A Java compiler is encouraged to issue a warning if a requires directive refers to an automatic module. An especially strong warning is recommended if the transitive modifier appears in the directive.

我首先回答你的实际问题 ("What is an automatic module?"),但我也会解释它们在那里 for。很难理解为什么自动模块在没有这些信息的情况下会以它们的方式运行。

什么是自动模块?

模块系统从它在模块路径上找到的每个 JAR 创建一个模块。对于模块化 JAR(即那些带有模块描述符的 JAR),它们定义 the module's properties (name, requires, exports). For plain JARs (no module descriptor) this approach doesn't work, so what should the module system do instead? It automatically creates a module - an automatic module 时很简单,可以这么说 - 并对三个属性进行最安全的猜测。

姓名

导出名字是一个two-step过程:

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

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

需要

由于普通 JAR 不表达 requires 子句,模块系统让自动模块读取进入 readability graph(也称为模块图)的所有其他模块。与显式模块不同,自动模块还读取未命名模块,其中包含从 class 路径加载的所有内容。这个看似微不足道的细节其实非常重要(见下文)。

自动模块还有一些更进一步的可读性怪癖:

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

总而言之,这可能会产生不幸的影响,即依赖于几个普通 JAR 的显式模块(即 non-automatic 模块)可以只需要其中一个(只要其他的结束)也在模块路径上)。

Exports/Opens

由于 JAR 不包含哪些包被视为 public API 哪些不是的信息,模块系统导出所有包并打开它们进行深度反思。

更多

模块系统也会扫描META-INF/services并让自动模块提供其中命名的服务。假定允许自动模块使用所有服务。

最后,Main-Class manifest 条目也被处理,所以定义一个的普通 JAR 可以像自动模块一样启动,其中主要 class 设置为 jar 工具(即 java --module-path my-app.jar --module my.app)。

合适的模块

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

什么是自动模块 for?

引入模块的原因之一是使编译和启动应用程序更加可靠,并更快地发现 class 路径可能出现的错误。其中一个关键方面是 requires 子句。

为了保证它们的可靠性,除了命名模块之外,模块声明无法要求任何其他内容,命名模块排除了从 class 路径加载的所有内容。如果故事到此结束,一个模块化 JAR 只能依赖于其他模块化 JAR,这将迫使生态系统自下而上模块化。

不过,这是不可接受的,因此引入了自动模块作为模块化 JAR 依赖于 non-modular 的一种方式,为此您需要做的就是将普通 JAR 放在模块路径上并要求模块系统给它起的名字。

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

您的模块可以位于一侧,需要它们的直接依赖作为自动模块,而间接依赖可以保留在另一侧。 每次您的一个依赖项变成一个显式模块时,它都会将桥留在模块化端,并将其直接依赖项作为自动模块绘制到桥上。