JMOD 的用途没有得到很好的记录,现有的文档也很少。根据我的理解,这是对系统的深入解释。
警告:此答案的部分内容相当长、冗长、部分冗余且难以阅读。非常欢迎进行建设性、结构性或语法编辑,以提高未来读者的可读性。
简短的回答
Java 9 的新模块系统Project Jigsaw 引入了新的可选链接时间 阶段的概念,该阶段发生在使用 CLI 工具 jlink 构建自定义空间优化 JRE 时. jlink 将所有显式/传递 JAR 模块/JMOD 依赖项捆绑到一个缩小的 JRE 中;依赖图中的所有其他无法访问的依赖项(从指定的根模块开始)不捆绑到构建的 JRE 中。从 JDK 9+ 开始,Java 的所有标准库都被分解为 JMOD,位于 <jdk>/jmods。
JAR 只能包含 .class 和资源文件,而 JMOD(即 .jmod 文件)包含在新的可选 链接时间 阶段专门用于自定义 JRE(例如可执行文件、本机库、配置、合法许可等)。这些附加文件在运行时不能作为资源在类路径中使用,而是安装在构建的 JRE 中的不同位置(例如,可执行文件和本机库位于 <jre>/bin 下)。从相关捆绑的 JAR 和 JMOD 依赖项中,类和文件资源将被写入单个优化的 JIMAGE 文件,位于 <jre>/lib/modules(在 Java 8 和之前的版本中替换 <jre>/lib/rt.jar)。 JMOD 的作用是在编译时和链接时,并且不设计为在运行时使用。
对于一般的库/应用程序,应该只构建和推送 JAR,而不是 JMOD;只有在特定条件下,JMOD 才会提供 链接时间 阶段所需的关键功能。在撰写本文时,除了 alpha 版本插件 org.apache.maven.plugins:maven-jmod-plugin,Maven 似乎没有对 JMOD 提供强大的支持。
长答案
这个冗长的答案的动机更为复杂,并为新模块系统的基本运作方式提供了一些启示。整篇文章都非常强调 CLI 工具 jlink,因为 JMOD 是专门为该工具引入的这个新的可选链接时间阶段而设计的。
拼图项目介绍
Java 9 在“JEP 261: Module System”中引入了Project Jigsaw,这是一种新颖的模块系统,可用于最小化启动时间和 JRE 的大小。作为此版本的一部分,引入了 CLI 实用程序 jmod、jimage 和 jlink 以及 JMODs/.jmods(基于 ZIP)和 JIMAGEs/.jimages 的新文件格式。
这个新模块系统的一个重要收获是 CLI 工具 jlink 使开发人员能够构建一个自定义 JRE,该 JRE 仅包含其应用程序的相关标准库和外部依赖项。这在compile time -> run time 管道中的传统阶段之间引入了一个可选的链接时间 阶段的新概念。
以使用 jlink 的优势为例,从 JDK 15 构建的极简 JRE 仅包含 java.base 模块,大小约为 40MB,与 JDK 15 的约 310MB 大小形成鲜明对比。这对于发布最小的自定义 JRE(例如精简 Docker 映像)特别有用。新的模块系统为 Java 生态系统带来了显着的好处,这些好处已经在别处详细讨论过,因此这里不再详细阐述。
3 J:JAR、JMOD 和 JIMAGE
JARs、JMODs 和 JIMAGEs 的高级描述并不能很快解释这三种文件格式的作用。以下是每个目的的非详尽概述:
-
JAR:基于 ZIP 文件格式的经典格式,用于在运行时将类和资源捆绑到类路径中。这是自 1997 年 JDK 1.1 以来制定的事实上的主流标准。可以使用 java -cp/-classpath 标志将 JAR 添加到类路径中。几乎每个库或依赖项有,是,并且将使用这种格式,所以在本节中略过。
-
JMODs:一种基于 ZIP 文件格式的新格式,用于捆绑 JAR 可以包含的相同内容,但支持其他文件(例如可执行文件、本机库、配置、合法许可证、等)在构建自定义 JRE 时在可选的链接时间阶段使用。 JMOD 设计用于编译时和链接时,但不在运行时使用。很可能引入了这种新格式(而不是扩展 JAR),因为在这种基于存档的新格式中的目录具有特殊含义,它不向后兼容已经使用相同目录名称的 JAR。
- 可以使用 CLI 工具
jmod 从 JAR 模块(即包含有效的 module-info.class)构建 JMOD。
- 从 JDK 9 起,所有 Java 标准模块都存储在 JDK 安装中的
<jdk>/jmods 下。
- JMOD 可以发布以供其他开发人员和上游应用程序使用;在撰写本文时,我不确定 JMOD 是否可以推送到 Maven 存储库,但各种来源似乎表明暂时不会。
- JMOD 类和资源不能在带有
java -cp/-classpath 标志的类路径中运行时使用,作为里面的类和资源的 JMOD 存档存储在 classes 下,而不是存档根目录中。
注意:可能有一种方法可以在运行时轻松地将 JMOD 添加到类路径中;然而,研究并没有明确说明与此相关的任何功能。仅向类路径添加 JMOD 不足以使用类和资源。然而,自定义ClassLoader 可用于在运行时正确解析 JMOD 存档中的类和资源文件;通常不建议这样做,也不是 JMOD 的目的。
-
JIMAGEs:在“JEP 220: Modular Run-Time Images”中引入的一种特殊文件格式,它是一个运行时图像,其中包含 JRE(即标准库)的所有必要类和资源。在 JRE/JDK 9 之前,使用单个大型非模块化 uber JAR,位于
<jre>/lib/rt.jar;它已被删除,取而代之的是存储在 <jre>/lib/modules 的单个优化 JIMAGE。此格式不是基于 ZIP 格式,它使用的自定义格式比原来的旧 JAR 格式更节省时间和空间,从而减少了启动时间。
- 使用 CLI 工具
jlink 构建自定义 JRE 映像时,所有相关(显式或传递)模块依赖项的类和资源(来自 JAR 模块或 JMOD)都被编译到单个优化的 JIMAGE 文件中(同样,存储在<jre>/lib/modules)。
- JIMAGE 文件格式是模块化的,可以使用 CLI 工具
jimage 创建、修改、反汇编或检查。例如。 jimage list $JAVA_HOME/lib/modules
- JIMAGE 通常不应发布,而是随特定的自定义 JRE 版本一起发布;文件格式将来可能会发生变化。
物质:JMOD 的详细用途
一个新的、可选的链接时间阶段
如前所述,CLI 工具jlink 在普通 Java 管道中引入了一个新的可选阶段 - 链接时间阶段。此链接时间阶段用于从一组 Java 9 模块(带有 module-info.java 描述符的 JAR 或 JMOD)生成自定义构建的 JRE。
高级阶段简述如下:
-
编译时间 (javac):如javac documentation所述,编译时间阶段...
...读取用 Java 编程语言编写的类和接口定义,并将它们编译成字节码类文件。它还可以处理Java源文件和类中的注解。
-
链接时间 (jlink):如“JEP 282: jlink: The Java Linker”所述,链接时间阶段是...
...编译时(javac 命令)和运行时(java 运行时启动器)阶段之间的可选阶段。链接时需要一个链接工具来组装和优化一组模块及其传递依赖项,以创建运行时映像或可执行文件。
链接时间是一个进行全局优化的机会,否则这些优化在编译时很难或在运行时代价高昂。一个例子是当所有输入都变为常数(即,不是未知的)时优化计算。后续优化将是删除不再可访问的代码。
-
运行时间 (java):如javac documentation 所述,运行时间阶段...
...启动 Java 应用程序。它通过启动 Java 运行时环境 (JRE)、加载指定的类并调用该类的 main() 方法来完成此操作。
JMODs介绍
在链接时间阶段,来自模块的所有类和资源(有效的 JAR 模块或形成 JMOD 的classes)都被编译成位于<jre>/lib/modules 的单个优化的 JIMAGE 运行时映像。未显式或传递地包含的模块将不包含在此最终 JIMAGE 中,从而节省大量空间。但是,在构建自定义 JRE 时,可能需要在 JRE 中添加一些附加文件;例如可执行命令或本机库。对于 JAR 模块,故事到此结束 - JAR 无法毫无歧义地将文件(超出 JIMAGE 中包含的类)添加到构建的 JRE 中。
JMODs 简介:JMODs 能够将其他文件添加到自定义构建的 JRE 中;一些示例(但不一定详尽):可执行命令、配置文件、头文件、法律声明和许可证、本机库和手册页。这允许模块依赖以自己的方式塑造构建的 JRE。这些附加文件如何通过 CLI 工具 jlink 插入到构建的 JRE 中的行为将在下一节中介绍。
JMOD 仅用于编译时间和链接时间阶段的,如“JEP 261: Module System”中所述:
JMOD 文件可以在编译时和链接时使用,但不能在运行时使用。通常,要在运行时支持它们,我们需要准备好动态提取和链接本机代码库。这在大多数平台上都是可行的,尽管它可能非常棘手,而且我们还没有看到很多需要此功能的用例,因此为简单起见,我们选择在此版本中限制 JMOD 文件的实用程序。
新格式 - 不与 JAR 向后兼容
一个很好的问题可能是“为什么不启用 JAR 来添加链接时行为?”。一个偷偷摸摸的怀疑是,这并不能为现有的 JAR 和工具提供足够的向后兼容性支持。 JAR 归档文件格式中没有保留文件名的规范。如果现有库将任何资源存储在用于链接时的目录下,jlink 无法准确猜测它是在链接时使用还是在运行时需要。具有保留目录名称的新文件格式规范将解决这个冲突问题 - 例如新的 JMOD 格式。使用 JMOD,对于链接时间和运行时间指定的资源没有歧义。此外,JMOD 格式还可以扩展以在以后的 JDK 版本中添加新功能,而不会出现向后兼容性问题。
JMOD 文件格式类似于 JAR,因为它基于 ZIP 文件格式。 JMOD 文件具有以下保留目录名称,具有以下行为(这不一定是详尽的列表!):
-
bin(--cmds):复制到<jre>/bin的可执行命令
-
classes (--class-path):用于包含到最终构建的 JIMAGE 中,存储在 <jre>/lib/modules
-
conf(--config):附加配置复制到<jre>/conf;如果需要,可能用于控制任何捆绑模块的配置
-
include (--header-files):复制到 <jre>/include/ 的附加 C 头文件,用于使用 JNI 为 JVM 构建 C 库;例如在java.base 中,JNI 接口被导出
-
legal (--legal-notices):复制到<jre>/legal/<module name>/ 的模块的法律声明和许可证
-
lib (--libs):复制到 <jre>/bin 的本机库
对于那些好奇的人来说,标准库 JMOD(在 JDK 9+ 中位于 $JAVA_HOME/jmods 下)可以使用任何读取 ZIP 存档的应用程序进行检查。
主流支持...?
JMOD 没有被迅速采用并且文档可用性较差的一个重要原因是,很简单地说,它们对于绝大多数库和模块依赖项来说不是必需的。尽管它们对于特定用例仍然有用,但模块应使用自 1997 年在 JDK 1.1 中定义以来已经获得主流支持的 JAR 格式(2017 年在 JDK 9 中添加了module-info.java 模块支持)。
来自CLI工具jmod的文档:
对于大多数开发任务,包括在模块路径上部署模块或将它们发布到 Maven 存储库,请继续将模块打包到模块化 JAR 文件中。 jmod 工具适用于具有本机库或其他配置文件的模块,或者适用于您打算使用 jlink 工具链接到运行时映像的模块。
意见:JMOD 可能不会在至少 非常 很长一段时间内获得开发人员的任何重大采用。大多数开发人员永远不会听到或知道 JMOD 的用途——他们也不需要。 JMOD 在幕后起到了构建 JRE 的关键作用(所有 Java 标准库模块都是 JMOD),但由于它们在链接时的利基用例,它们不会影响绝大多数应用程序和项目。 Java 9 于 2017 年发布,Java 生态系统中的依赖项仍然难以可靠地拥有 module-info.class 描述符以使 JAR 成为有效的成熟模块...
要点
- JMOD 是使用 CLI 工具
jlink 创建 JRE 的一项基本新功能,可使用其他文件自定义构建的 JRE。
- 部署 JAR 而不是 JMOD,除非特别需要 JMOD 中的某些功能。 JAR 模块也与
jlink 兼容,因此无需发布仅包含类和资源的 JMOD。生态系统支持和工具不一定会很快采用 JMOD,而且在未来几年肯定会出现兼容性问题。
- 该生态系统领域的 Java 文档确实可以使用一些改进。
免责声明
在撰写此答案时,关于 Java 9 及更高版本的 JMOD 用途的文档很少。事实上,Google 搜索短语“java jmods”和“jmod format”带来了与第二次搜索命中结果相同的 StackOverflow 问题。因此,有些方面可能解释不准确,但总体上是“方向正确的”;此外,它可能无法描绘出全貌。如果您发现任何问题或警告,请发表评论,我会尝试将其与此答案相协调。