关键看JAR是否有module-info.class:有则必须放--module-path,否则模块语义失效;无则只能放-cp,强行入-module-path会转为无封装的自动模块,易致split package错误。
区分模块路径与类路径,关键看jar里有没有 module-info.class;解决启动时的变量加载冲突,核心是让jvm按预期方式加载资源,不混用路径、不跨域引用。
看JAR有没有 module-info.class
这是唯一硬性判断标准:
- 有
module-info.class→ 必须放--module-path(或-p),否则模块声明失效,requires和exports全部被忽略 - 没有
module-info.class→ 只能放-cp(或--class-path);若强行塞进--module-path,JVM会把它当作“自动模块”,模块名默认是 JAR 文件名(如guava-31.1-jre.jar→ 模块名guava.31.1.jre),但包无封装、无依赖约束,容易引发 split package 错误 - 用命令快速验证:
jar -tf your.jar | grep module-info.class
启动报错时先定位路径归属
常见报错本质都是路径配置错位:
-
Module not found: xxx→requires xxx;写了,但xxx的 JAR 没出现在--module-path中;它不会从-cp里找 -
Unable to derive module descriptor→ JAR 里没module-info.class,又没用--add-modules显式声明模块名,JVM无法识别其身份 -
NoClassDefFoundError或IllegalAccessError→ 类在-cp里,但代码在模块内通过反射访问了未opens的包,或用了Class.forName加载模块内类却绕过了模块系统
混合项目中避免变量/资源加载冲突
当项目既有模块化模块,又有传统库(比如 Spring Boot 3.x 含模块声明,但某些老工具包仍是普通 JAR),需严格隔离路径用途:
- 把模块化 JAR 全部放进
--module-path,非模块化 JAR 全部放进-cp,两者不交叉 - 模块内的代码无法直接访问
-cp中的类(除非该模块requires一个自动模块,且对方确实导出了包);反过来,-cp中的类可通过Class.forName加载模块里的 public 类,但所有模块语义(如封装、服务发现)全部丢失 - 同名类同时存在于两个路径时,
--module-path优先,-cp中那个被静默忽略——哪怕它是你真正想用的新版本 - 检查是否漏加平台模块:比如用了
java.logging,但没在启动参数中加--add-modules java.logging,尤其在自定义运行镜像时容易出问题
构建与调试建议
落地时别只靠经验猜,用工具确认:
- 启动前用
java --list-modules | grep xxx看目标模块是否已加载 - Maven 项目执行
mvn dependency:tree -Dverbose查看哪些依赖含module-info.class(通常标注为modulescope) - IDE 中(如 IntelliJ)启用 “Use --module-path for modules” 选项,并关闭自动 patch classpath 到 module-path 的行为
- 测试阶段加 JVM 参数
--show-module-resolution,启动时打印模块解析过程,一眼看出哪个模块没找到或被跳过

















