应先用mvn dependency:tree -dincludes=groupid:artifactid定位指定组件所有路径和版本,再用mvn dependency:tree -dverbose查看被仲裁掉的版本及原因,最终通过-verbose:class确认jvm实际加载的类来源。

怎么看清楚到底加载了哪个版本的 Jar
类加载异常(比如 NoSuchMethodError 或 ClassNotFoundException)发生时,第一件事不是改 pom.xml,而是确认 JVM 实际加载的是哪个版本。Maven 的依赖调解规则只是决定了「编译期选哪个」,但运行时类加载器是否真加载了它,得靠实证。
推荐组合使用两个命令:
-
mvn dependency:tree -Dincludes=groupId:artifactId:快速定位指定组件在依赖树中的所有路径和版本,例如mvn dependency:tree -Dincludes=org.mapstruct:mapstruct -
mvn dependency:tree -Dverbose:显示所有冲突节点(带 * 标记),并标出被仲裁掉的版本及原因(如 “omitted for conflict with 1.3.0”)
注意:-Dverbose 输出里出现 “omitted for duplicate” 并不等于安全——它只说明 Maven 没把那个 jar 打进 classpath,但如果该 jar 被其他方式(如 fat jar 手动打包、容器镜像预置)混入运行环境,仍会触发类加载冲突。
为什么 exclude 排除后还是报错
用 <exclusion></exclusion> 排除了某个传递依赖,但运行时依然抛出 NoClassDefFoundError,常见原因有三个:
- 排除位置错了:只在 A 依赖里排除了
commons-lang3,但 B 依赖也传入了同名不同版本,且路径更短,最终生效的仍是 B 带来的那个版本 - 排除不彻底:被排除的 artifactId 写错(比如写成
commons-lang而非commons-lang3),或漏写了<groupid></groupid>,导致 exclusion 未生效 - IDE 缓存未刷新:IntelliJ IDEA 即使执行了
Reload project,有时也不会重载 classpath,需手动File → Invalidate Caches and Restart
验证 exclusion 是否生效,最直接的方式是执行 mvn dependency:tree -Dincludes=org.apache.commons:commons-lang3,看输出中是否还有该坐标 —— 如果还有,说明没排除干净。
什么时候该用 dependencyManagement 锁定版本
当项目中多个模块(如 service、gateway、job)各自声明了同一依赖(如 net.sf.ehcache:ehcache)但版本不一致时,<dependencymanagement></dependencymanagement> 是唯一可控手段。它不引入依赖,只统一“承诺版本”,所有子模块引用时自动采用该版本,绕过最短路径/声明顺序规则。
关键点:
- 必须放在父 POM 的
<dependencymanagement></dependencymanagement>块中,子模块的<dependencies></dependencies>里只写 groupId/artifactId,不写 version - 如果子模块显式写了 version,会覆盖
dependencyManagement中的声明(这是设计行为,不是 bug) - 对 Spring Boot 项目,优先用
spring-boot-dependencies提供的 BOM(如spring-boot-starter-parent),它已内置大量依赖版本约束,比手写dependencyManagement更稳妥
线上 ClassCastException 往往不是 Maven 冲突,而是类加载器隔离失效
本地跑得好,上线就抛 ClassCastException: com.example.User cannot be cast to com.example.User?这大概率不是 Maven 版本选错,而是同一个类被两个不同类加载器加载(比如 Tomcat 的 WebAppClassLoader 和 shared lib 的 URLClassLoader 各自加载了一次 user.jar)。
这种问题不会出现在 mvn dependency:tree 输出里,排查路径是:
- 检查部署包结构:确认
WEB-INF/lib下没有重复的 jar(尤其是被provided作用域误打进来的) - 确认容器配置:Tomcat 的
shared.loader或 Spring Boot 的loader.path是否意外引入了同名 jar - 加 JVM 参数验证:
-verbose:class输出类加载日志,搜索目标类全限定名,看它被哪个 loader 加载、从哪个路径加载
真正棘手的冲突,从来不在依赖树里,而在 classpath 和类加载器的交界处。








