Java 中的 JMOD 文件格式详解
1. 概述
在本教程中,我们将深入探讨 JMOD 是什么,以及它与 JAR 文件有何不同。随后,我们将创建一个简单的模块化项目,将其打包为 JMOD 文件,并使用 jlink 生成一个专门针对该应用程序量身定制的最小化 Java 运行时环境。
2. 什么是 JMOD?
在 Java 9 之前,Java 应用程序主要通过 Maven 和 Gradle 等构建工具打包为 JAR 文件。随着 Java 9 引入 Java 平台模块系统 (JPMS),Java 拥有了正式的模块系统,并引入了 JMOD 文件格式。
JMOD 文件是一种 Java 模块打包格式,旨在用于编译和链接时,而非直接用于运行时。
与 JAR 文件不同,我们在运行应用程序时不能将 JMOD 文件放置在类路径(classpath)或模块路径(module path)上。
和 JAR 文件一样,JMOD 文件可以包含已编译的类文件和资源。但不同的是,JMOD 文件还可以包含其他构件,如原生库、配置文件和法律声明。
2.1 与 JPMS 的关系
此外,JMOD 与 JPMS 联系紧密。它是那些可能不仅仅需要已编译字节码文件的模块的打包格式。
在 JPMS 架构中,源代码被编译为字节码。然后,这些字节码被打包成模块(JAR 或 JMOD)。接着,在链接阶段,我们可以使用 jlink 将这些模块组装成自定义的运行时镜像。
链接阶段是编译和执行之间的过程。在此阶段,Java 链接器会分析模块依赖关系,并仅将所需的模块捆绑到运行时镜像中。JMOD 文件正是专门为参与此链接过程而设计的。
2.2 JMOD 的用途及其与模块化 JAR 的区别
虽然 JPMS 支持模块化 JAR 文件,但引入 JMOD 是为了解决 JAR 打包的局限性。模块化 JAR 包含已编译的类和资源,可以直接在运行时使用。
另一方面,JMOD 文件可能包含原生代码和额外的元数据。它专用于链接时处理,不能直接执行。与 JAR 文件不同,它不打算发布到 Maven Central 等仓库中。
其主要目的是支持使用 jlink 创建自定义运行时镜像。例如,将应用程序与完整的 JRE 一起打包会显著增加分发包的大小。而 JMOD 允许创建一个仅包含应用程序所需模块的自定义 Java 运行时,从而实现更小、更精简的分发包。
3. 实践示例:创建自定义 JRE
为了更好地理解 JMOD 文件格式的工作原理,我们来构建一个简单的模块化 Java 应用程序,并用它创建一个自定义运行时镜像。
3.1 示例应用程序
让我们引导一个简单的 Java 项目,将 "Hello Baeldung!" 打印到控制台:
在上面的代码中,我们定义了一个名为 Hello 的类,并使用 java.util.logging API 将消息记录到控制台。
接下来,通过在模块源码根目录下添加一个 module-info.java 文件,使该项目成为一个 Java 模块:
这里,com.baeldung.jmod_sample 是模块名称。虽然每个模块都隐式依赖于 java.base,但其他模块(如 java.logging)必须显式声明。
接下来,编译程序:
上述命令会查找 src 目录下的所有 .java 文件并进行编译,然后将生成的 .class 文件输出到 output 目录中。
3.2 打包为 JMOD
接下来,通过将其打包为 JMOD 文件,为创建自定义运行时做准备。
要创建 JMOD 文件,可以使用 jmod create 命令:
这里,--class-path output/ 指定了包含已编译类的目录。同时,我们定义了模块的入口点和生成的 JMOD 文件名。
执行该命令后,当前目录下会生成一个 hello.jmod 文件。
接下来,通过运行 jmod describe 命令来查看 JMOD 文件的内容:
这会将文件内容输出到控制台:
这证实了 JMOD 文件正确封装了编译后的模块及其元数据,使其可以在链接阶段通过 jlink 使用。
3.3 使用 jlink 创建自定义运行时
现在我们已经生成了 JMOD 文件,让我们使用 jlink 创建一个自定义 Java 运行时:
这里,--module-path $JAVA_HOME/jmods:hello.jmod 指定了 jlink 查找所需模块的位置。--add-modules 选项指明了要包含在运行时镜像中的根模块。
然后,我们使用 launcher 选项创建一个运行 Hello 类的启动脚本。命令执行后,jlink 会生成一个名为 "custom-runtime-min" 的新目录。
我们可以检查生成的运行时镜像的大小:
输出如下:
生成的运行时约为 35 MB,远小于通常超过 400 MB 的完整 JDK 安装包。
让我们验证一下哪些模块被捆绑到了运行时镜像中:
输出如下:
这证实了仅包含了所需的模块。
最后,运行生成的启动器:
上述命令输出:
应用程序成功地使用了该自定义运行时镜像运行。
4. 总结
在本文中,我们了解了什么是 JMOD 文件、它包含的内容、它与 JAR 文件的区别以及何时应该使用它。此外,我们构建了一个简单的模块化 Java 应用程序,将其打包为 JMOD 文件,并使用 jlink 生成了一个专门为我们的应用程序量身定制的精简自定义 Java 运行时镜像。
一如既往,该示例的完整源代码可在 GitHub 上找到。