Java类冲突也就是常说的Jar Hell,是指项目中引入了同一个类的多个不同版本,或者不同Jar包中包含全限定名相同的类,导致JVM加载类时出现预期之外的行为,常见的表现有ClassNotFoundException、NoSuchMethodError、NoClassDefFoundError等运行时异常,这类问题在依赖复杂的项目中十分常见。

Java类冲突的常见产生原因
类冲突的核心原因是类路径中出现了多个版本的同名类,常见的场景有以下几种:
- 项目直接依赖了同一个Jar包的不同版本,比如同时引入了commons-lang3的3.8版本和3.12版本
- 项目依赖的多个第三方Jar包,间接依赖了同一个Jar包的不同版本,比如A包依赖log4j 1.x,B包依赖log4j 2.x
- 不同功能Jar包中包含了全限定名完全相同的类,比如两个不同团队的组件都定义了com.demo.User类
使用Maven依赖树定位冲突来源
Maven提供了内置的命令可以查看项目的完整依赖树,帮助开发者快速找到冲突的依赖路径。最常用的命令是mvn dependency:tree,执行后会输出项目所有依赖的层级关系。
基础依赖树查看
在项目根目录执行以下命令,就可以得到完整的依赖树输出:
mvn dependency:tree
输出结果中,每个依赖会显示groupId、artifactId、版本号,以及依赖的来源路径,比如下面的片段表示项目依赖的spring-core是通过spring-context间接引入的:
[INFO] com.demo:test-project:jar:1.0-SNAPSHOT [INFO] +- org.springframework:spring-context:jar:5.3.20:compile [INFO] | - org.springframework:spring-core:jar:5.3.20:compile [INFO] | - org.springframework:spring-jcl:jar:5.3.20:compile
过滤特定依赖的树
如果项目依赖很多,直接看完整树很难找到目标依赖,可以使用-Dincludes参数过滤指定groupId或者artifactId的依赖,比如要查看所有log4j相关的依赖:
mvn dependency:tree -Dincludes=org.apache.logging.log4j
如果要查看冲突的同一个Jar包的所有版本,也可以直接过滤该Jar的artifactId,快速定位所有引入路径。
Maven依赖排除解决类冲突
找到冲突的依赖来源后,就可以通过Maven的依赖排除功能,去掉不需要的版本依赖。依赖排除需要在引入依赖的pom.xml中配置<exclusions>标签。
排除间接依赖示例
假设项目依赖了A包,而A包间接引入了不需要的1.0版本的B包,我们需要在引入A包的时候排除B包的依赖,配置如下:
<dependency>
<groupId>com.demo</groupId>
<artifactId>A</artifactId>
<version>2.0</version>
<exclusions>
<exclusion>
<groupId>com.demo</groupId>
<artifactId>B</artifactId>
</exclusion>
</exclusions>
</dependency>
注意exclusion标签中不需要写版本号,只要指定groupId和artifactId就可以排除该依赖的所有版本。
统一依赖版本
如果是同一个Jar包的不同版本冲突,除了排除低版本,还可以通过<dependencyManagement>统一指定该Jar包的版本,避免不同依赖引入不同版本:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
</dependencies>
</dependencyManagement>
这样所有引入commons-lang3的依赖都会使用3.12.0版本,不会出现版本冲突。
冲突解决后的验证方法
完成依赖排除或者版本统一后,可以再次执行mvn dependency:tree命令,确认冲突的依赖已经被移除或者版本已经统一。之后可以执行mvn clean package打包项目,运行程序验证之前的类冲突异常是否已经消失。
另外也可以使用mvn dependency:analyze命令分析项目依赖,查看是否有未使用的依赖或者缺失的依赖,进一步优化依赖配置。