split package在jpms中编译期直接报错而非静默通过,因模块系统强制要求包名唯一归属;错误提示“declared in more than one module”表明多个模块导出同一包,“not the current module”则揭示跨模块访问未正确声明依赖。

Java 模块化系统中,拆分包(Split Package)问题不会在编译期“静默通过”,而是直接报错:error: package is declared in module 'X', which is not the current module 或 package is declared in more than one module。这不是运行时隐患,而是 JPMS 在编译阶段就强制拦截的架构红线——它拒绝模糊归属,只允许一个模块拥有对某个包的导出权。
看清报错本质:不是“找不到”,而是“不允许多个声明”
当两个模块(比如 utils-core 和 utils-ext)都包含 com.example.utils 包,且各自在 module-info.java 中写了 exports com.example.utils;,javac 就会在编译任一模块时立即失败。JPMS 不做合并、不选优先级,只认唯一性。
- 错误信息里出现 “declared in more than one module” → 说明多个命名模块同时导出同一包
- 错误信息里出现 “declared in module 'X', which is not the current module” → 说明当前模块试图访问的类,实际来自另一个也声明了该包的模块,但未被正确 requires 或 exports
- 注意:自动模块(automatic modules)也可能引发此错——如果两个 JAR 都含
com/example/utils/目录,且都被当作模块加载,JPMS 同样视为分裂包
三步定位物理冲突来源
别猜哪个模块“多写了”,用工具确认真实路径:
- 对每个疑似模块,执行
jar -tf xxx.jar | grep "com/example/utils/",看哪些 JAR 确实打包了该路径 - 在 Java 运行时加启动参数
--show-module-resolution,启动时会打印每个包由哪个模块提供 - 用
jdeps --multi-release 17 --summary your-app.jar扫描所有依赖,输出中若出现重复包名,即为分裂点
根治方案:统一归属,切断多源出口
修复目标不是“保留 A 或删掉 B”,而是让 com.example.utils 只有一个权威出口:
- 将其中一个模块(如
utils-ext)中的com.example.utils包整体迁出,重命名为com.example.utils.ext,同步更新所有import和module-info.java中的exports - 保留一个核心模块(如
utils-core)作为唯一导出者,其他模块改用requires utils.core;+import com.example.utils.*;,不再自带同名包 - 若涉及第三方非模块化 JAR(如老版本 commons-lang3),用
org.javamodularity.moduleplugin插件将其转为规范自动模块,并确保整个项目中仅引入该 JAR 的一个版本
构建阶段主动拦截(防复发)
光靠人眼检查不可靠,把验证纳入 CI:
- Maven 项目添加
maven-enforcer-plugin,启用banDuplicateClasses规则,构建时扫描重复类和包路径 - Gradle 项目在
build.gradle.kts中配置tasks.withType<javacompile>().configureEach</javacompile>,强制开启--module-source-path并校验 exports 唯一性 - 在 IDE 的编译器设置中启用 “Show all module-related warnings”,让分裂包提示在编码阶段就浮现
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











