自动模块名由jar文件名派生:去.jar和版本后缀,非字母数字字符换为点号,数字开头加system.;易致冲突,应重命名jar或用--add-modules显式指定。

当第三方 JAR 包不含 module-info.class 时,Java 9+ 会将其视为 自动模块(Automatic Module),其模块名默认由 JAR 文件名派生。这个派生过程不是随意的,而是有明确规则的——它直接影响 requires、exports 和运行时类加载行为,尤其在混合使用命名模块与自动模块的项目中容易出错。
自动模块名的派生规则
Java 运行时依据 JAR 文件名生成模块名,核心逻辑如下:
- 去掉文件扩展名(
.jar)和版本后缀(如-1.2.3、-SNAPSHOT、-final等) - 将剩余部分中的非字母数字字符(如
-、_、.)统一替换为点号. - 确保结果不以数字开头;若开头是数字,则在前面加
system. - 最终名称必须是合法 Java 标识符(不能含空格、特殊符号,不能是关键字)
例如:guava-32.1.3-jre.jar → guava;my-utils_v2.0.jar → my.utils.v2;3rd-lib-1.0.jar → system.3rd.lib。
常见问题与规避方式
派生名可能引发冲突或不可预期行为,比如:
- 多个 JAR 派生出相同模块名(如
log4j-api-2.20.0.jar和log4j-core-2.20.0.jar都变成log4j),导致模块图解析失败 - 派生名含非法字符(如
my.plugin-1.0.jar→my.plugin,但plugin是保留字?实际不冲突,但易混淆) - 模块名太短(如
a.jar→a),与其他模块或 JDK 内置模块重名
解决方法:显式指定模块名,避免依赖自动派生。
强制指定自动模块名(推荐做法)
在 module-info.java 中无法直接“命名”自动模块,但可通过 JVM 启动参数干预:
- 使用
--add-modules显式声明需解析的自动模块名(如--add-modules guava) - 更可靠的是用
--patch-module或构建时重命名 JAR 文件,使其派生名符合预期(如将mylib-1.0.jar改为com.example.mylib-1.0.jar) - Maven 构建中可配合
maven-shade-plugin或jarjar重打包,并设置规范的 artifactId,让最终 JAR 名天然适配模块命名规则
验证与调试技巧
运行时确认模块名是否符合预期:
- 启动时加
--list-modules查看所有已解析模块及其名称 - 用
jdeps --summary your-app.jar观察自动模块依赖关系 - 在代码中调用
ModuleLayer.boot().modules()遍历并打印模块名,定位命名异常
如果发现两个 JAR 被识别为同一模块名,必须修改其中一个的文件名或通过工具重打包隔离。










