自动模块是传统JAR在模块路径上的过渡形态,名称由MANIFEST中Automatic-Module-Name指定或按文件名推导,默认导出所有包但不解决封装、反射及SPI等问题。

自动模块机制不是迁移终点,而是过渡桥梁——它让未带 module-info.class 的传统 JAR 在模块路径下可被引用,但不解决封装、反射或服务加载等深层问题。关键在于“用得稳”,而非“用得快”。
识别并确认自动模块行为
把一个普通 JAR 放进模块路径(如通过 --module-path 启动),JVM 就会自动赋予它模块身份。名称生成规则如下:
- 若 JAR 的
META-INF/MANIFEST.MF中声明了Automatic-Module-Name: com.example.lib,就直接采用该名 - 否则按文件名推导:去掉扩展名和版本号,转小写,用点分隔符替换短横线和下划线
例如:guava-32.1.3-jre.jar→guava.32.1.3.jre;log4j-core-2.20.0.jar→log4j.core.2.20.0 - 名称含数字或特殊字符易引发冲突,建议优先在构建时注入
Automatic-Module-Name
规避自动模块常见陷阱
自动模块默认导出所有包,看似方便,实则破坏模块封装原则,还可能造成包泄露或 split-package 错误:
- 避免多个自动模块导出同名包(如两个不同版本的
commons-lang3同时出现在模块路径) - 检查是否与显式模块存在包重叠:用
jdeps --print-module-deps your-app.jar查看自动模块列表,再用jdeps -s --module-path lib/ your-app.jar分析包级依赖 - 若发现
split package警告,必须移除重复包来源,或改用--patch-module合并补丁(仅限调试阶段)
配合 JVM 参数补足能力缺口
自动模块本身不开放反射、不声明 SPI、不处理资源加载——这些需手动干预:
- 反射访问受限类(如
sun.misc.Unsafe或内部工具类):添加--add-opens java.base/sun.misc=ALL-UNNAMED - 使用
ServiceLoader加载服务实现类:在模块声明中补uses com.example.spi.MyService;,否则即使类在类路径上也找不到 - 跨模块读取
META-INF/services/或配置文件:确保资源所在模块已opens对应包给调用方,或改用Class.getResource()配合类加载器策略
构建阶段主动治理依赖
别等运行时报错才处理。Maven 或 Gradle 中应提前控制自动模块质量:
- Maven:在
pom.xml中为关键依赖配置<properties></properties>注入 MANIFEST 属性:<manifestentries><automatic-module-name>com.google.guava</automatic-module-name></manifestentries> - Gradle(Java Library 插件):在
jar任务中设置:manifest { attributes 'Automatic-Module-Name': 'org.apache.logging.log4j.core' } - 统一归档命名规范,避免
xxx-1.0-SNAPSHOT.jar这类无法稳定推导模块名的文件名

















