You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为jlink打包的JavaFX自定义运行时镜像添加依赖未包含JDK类的可插拔子模块

如何为jlink打包的JavaFX自定义运行时镜像添加依赖未包含JDK类的可插拔子模块

嗨,刚好之前我也折腾过类似的场景,来给你梳理几个可行的解决方案,帮你搞定这个可插拔子模块的问题😉

首先得明确核心矛盾:你用jlink打包的基础镜像是经过裁剪的,只包含了你当初打包时指定的那些模块,子模块如果用到了基础镜像里没有的JDK模块,直接跑肯定会报类找不到的错误。针对这个问题,有几个不同方向的解决思路,你可以根据自己的需求选:

1. 提前预留可能用到的JDK模块(最简单但不够灵活)

如果你能提前预判子模块可能会用到哪些JDK模块,比如java.sql、java.naming这类,那在最初用jlink打包基础镜像的时候,就把这些模块一起加进去就行。举个例子,原来的打包命令可能是这样:

jlink --module-path javafx-jmods:jdk-jmods --add-modules javafx.controls,javafx.fxml --output base-image

那现在就把需要的JDK模块加在--add-modules后面:

jlink --module-path javafx-jmods:jdk-jmods --add-modules javafx.controls,javafx.fxml,java.sql,java.naming --output base-image

这种方式的优点是子模块直接就能用,不需要额外配置;缺点也很明显——如果子模块用到的模块太多,基础镜像会变大,失去jlink裁剪体积的意义,而且如果后续子模块用到了新的未预留模块,还是得重新打包基础镜像。

2. 运行时动态加载子模块和依赖的JDK模块(灵活,不影响基础镜像)

如果想保持基础镜像的小巧,同时支持可插拔的子模块,那可以在运行主应用的时候,通过参数指定额外的模块路径和要加载的模块。这里分两种情况:

子模块是模块化JAR(带module-info.java)

如果你的子模块是符合Java模块化规范的,那在它的module-info.java里要声明好依赖的JDK模块,比如:

module com.your.submod {
    requires javafx.controls;
    requires java.sql; // 子模块依赖的JDK模块
    exports com.your.submod.api; // 暴露给主应用的API
}

然后你需要从完整JDK的jmods目录里,把这些缺失的JDK模块(比如java.sql.jmod)复制到一个单独的目录,比如submod-deps。之后运行主应用的时候,加上这些参数:

# Linux/macOS
./base-image/bin/java --module-path submod-deps:your-submod.jar --add-modules com.your.submod -m com.your.main.module/com.your.main.MainClass

# Windows
base-image\bin\java --module-path submod-deps;your-submod.jar --add-modules com.your.submod -m com.your.main.module/com.your.main.MainClass

这样JVM会在运行时动态加载这些额外的模块,完全不会改动原来的基础镜像。

子模块是非模块化JAR(传统JAR)

如果子模块还是传统的非模块化JAR,那可以用--class-path指定子模块的路径,同时用--add-modules手动加上它依赖的JDK模块:

# Linux/macOS
./base-image/bin/java --add-modules java.sql --class-path your-submod.jar -m com.your.main.module/com.your.main.MainClass

# Windows
base-image\bin\java --add-modules java.sql --class-path your-submod.jar -m com.your.main.module/com.your.main.MainClass

这种方式虽然也能工作,但不如模块化JAR的依赖管理清晰,建议尽量把子模块改成模块化的。

3. 用jlink扩展基础镜像(生成包含子模块的新镜像)

如果想实现“安装子模块后,用户直接运行镜像就能用,不需要额外参数”,那可以基于原来的基础镜像,用jlink重新打包,把子女模块和它依赖的JDK模块加进去。比如:

jlink --module-path base-image/modules:your-submod.jar:/path/to/full-jdk/jmods --add-modules com.your.submod,java.sql --output extended-image

这里的base-image/modules是原来基础镜像里的模块目录,/path/to/full-jdk/jmods是完整JDK的模块目录,--add-modules指定要添加的子模块和缺失的JDK模块。运行完这个命令后,生成的extended-image就是包含了子模块的新镜像,用户直接运行这个镜像里的启动脚本就行。


然后回答你最后一个问题:能不能添加多个增量的lib/modules文件给JVM?
完全可以!JVM的--module-path支持指定多个路径,路径之间用冒号(Linux/macOS)或分号(Windows)分隔。你可以把不同子模块的依赖模块分别放到不同的目录,比如submod1-deps、submod2-deps,然后在运行时这样指定:

--module-path submod1-deps:submod2-deps:submod1.jar:submod2.jar

JVM会自动扫描这些路径里的所有模块,按需加载,非常灵活。

最后给你几个小建议:

  • 尽量把子模块做成模块化JAR,这样依赖关系更清晰,JVM的模块加载机制能更好地帮你处理依赖问题
  • 可以用./base-image/bin/java --list-modules命令查看基础镜像里包含的所有模块,对比子模块需要的模块,快速找出缺失的部分
  • 如果子模块需要用到的JDK模块比较多,又不想提前预留,那运行时动态加载的方式会更适合你

备注:内容来源于stack exchange,提问作者du fei

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 16:53:10