Java: JMOD 文件格式详解
1. 概述
在本教程中,我们将深入探讨 JMOD 的含义及其与 JAR 文件的区别。随后,我们将创建一个示例模块化项目,将其打包为 JMOD 文件,并使用 jlink 生成一个专为该应用程序定制的最小 Java 运行时环境。
2. JMOD 是什么?
在 Java 9 之前,Java 应用主要通过 Maven 和 Gradle 等构建工具打包为 JAR 文件。随着 Java 9 中 Java 平台模块系统的引入,Java 获得了一个正式的模块系统,并引入了 JMOD 文件格式。
2.1. 它与 JPMS 的关系
此外,JMOD 与 JPMS 密切相关。它作为模块的打包格式,可能需要的不仅仅是编译后的字节码文件。
在 JPMS(Java Platform Module System,Java 平台模块系统)架构中,源代码被编译成字节码。随后,字节码被打包成模块(JAR 或 JMOD)。接着,在链接时,我们可以使用 jlink 将模块组装成一个自定义的运行时镜像。
链接时间是编译和执行之间的阶段。在此阶段,Java 链接器会分析模块依赖关系,并仅将所需的模块打包成一个运行时镜像。JMOD 文件是专门为参与链接过程而设计的。
2.2. JMOD 的目的及其与模块化 JAR 的不同之处
虽然 JPMS 支持模块化 JAR 文件,但 JMOD 的引入是为了解决 JAR 打包的局限性。模块化 JAR 包含已编译的类和资源,可在运行时直接使用。
另一方面,JMOD 文件可能包含本机代码和其他元数据。它旨在用于链接时处理,不能直接执行。与 JAR 文件不同,它并不打算发布到 Maven Central 等存储库中。
其主要目的是支持使用 jlink 创建自定义运行时镜像。例如,将应用程序与完整的 Java 运行时环境(JRE)打包在一起会显著增加分发包的大小。而 JMOD 则允许创建仅包含应用程序所需模块的自定义 Java 运行时环境,从而使得分发包更小、更易于初学者使用。
3. 实例:创建自定义 Java 运行时环境(JRE)
为了更好地理解 JMOD 文件格式的工作原理,让我们构建一个简单的模块化 Java 应用程序,并使用它来创建自定义运行时镜像。
3.1. 示例应用
让我们引导一个简单的 Java 项目,该项目会在控制台输出 “Hello World!”:
public class Hello {
private static final Logger LOG = Logger.getLogger(Main.class.getName());
public static void main(String[] args) {
LOG.info("Hello World!");
}
}在上述代码中,我们定义了一个名为 Hello 的类,并使用 java.util.logging API 将一条消息记录到控制台。
接下来,让我们在模块源代码目录的根目录下添加一个 module-info.java 文件,从而将项目打造成一个 Java 模块:
module com.baeldung.jmod_sample {
requires java.logging;
}在这里,com.baeldung.jmod_sample 是模块名称。虽然每个模块都隐式依赖于 java.base,但像 java.logging 这样的附加模块则必须进行显式声明。
接下来,让我们编译程序:
$ javac -d output $(find -name \*.java)上述命令会定位 src 目录中的所有 .java 文件并对其进行编译。然后,它将生成的 .class 文件输出到 output 目录中。
3.2. 打包到 JMOD
接下来,让我们将模块化应用打包成 JMOD 文件,以便进行自定义运行时的创建。
要创建 JMOD 文件,我们可以使用 jmod create 命令:
$ jmod create \
--class-path output/ \
--main-class com.baeldung.jmod_sample.Hello \
--module-version 1.0.0 -p output hello.jmod在这里,–class-path output/ 指定了包含已编译类的目录。同时,我们定义了模块的入口点以及生成的 JMOD 文件的名称。
执行该命令后,将在当前目录中创建一个 hello.jmod 文件。
接下来,让我们通过运行 jmod describe 命令来检查 JMOD 文件,看看它包含什么内容:
$ jmod describe hello.jmod这将文件内容输出到控制台:
com.baeldung.jmod_sample@1.0.0
requires java.base mandated
requires java.logging
contains com.baeldung.jmod_sample
main-class com.baeldung.jmod_sample.Hello这证实了 JMOD 文件正确地封装了编译模块及其元数据,使其可以在链接时使用 jlink 进行使用。
3.3. 使用 jlink 创建自定义运行时环境
既然我们已经生成了 JMOD 文件,接下来就使用 jlink 来创建一个自定义 Java 运行时环境:
$ jlink \
--module-path $JAVA_HOME/jmods:hello.jmod \
--add-modules com.baeldung.jmod_sample \
--launcher hello=com.baeldung.jmod_sample/com.baeldung.jmod_sample.Hello \
--strip-debug \
--compress=2 \
--no-header-files \
--no-man-pages \
--output custom-runtime-min在这里,–module-path $JAVA_HOME/jmods:hello.jmod 指定了 jlink 应在何处查找所需的模块。接下来,–add-modules 选项指明了要包含在运行时镜像中的根模块。
然后,我们使用启动器选项来创建一个运行 Hello 类的启动器脚本。执行该命令后,jlink 会生成一个名为 “custom-runtme-min” 的新目录。
我们可以检查生成的运行时镜像的大小:
$ du -sh custom-runtime-min以下是输出:
35M custom-runtime-min/最终的运行时大小约为 35MB,远小于完整的 JDK 安装文件,后者通常超过 400MB。
让我们验证一下哪些模块被打包到了运行时镜像中:
$ ./custom-runtime-min/bin/java --list-modules以下是输出:
com.baeldung.jmod_sample@1.0.0
java.base@25.0.2
java.logging@25.0.2这证实了只包含了所需的模块。
最后,让我们运行生成启动器:
$ ./custom-runtime-min/bin/hello上述命令生成:
Mar 01, 2026 10:15:38 AM com.baeldung.jmod_sample.Hello main
INFO: Hello Baeldung!使用自定义运行时镜像,应用程序运行成功。
4. 结论
在本文中,我们了解了 JMOD 文件是什么、它可以包含哪些内容、它与 JA R文件有何不同以及何时应使用它。此外,我们还构建了一个简单的模块化 Java 应用程序,将其打包成 JMOD 文件,并使用 jlink 生成了一个专为我们的应用程序量身定制的精简、自定义的 Java 运行时镜像。