本文涉及到如下几个概念需要注意:依赖冲突、依赖调解、依赖传递。
依赖冲突是什么
原来运行得好好的代码,在引入一个 Jar 包后出现 NoSuchMethodException、NoClassDefFoundError 等错误提示,那么恭喜你,大概率遇到了依赖冲突。
在认识依赖冲突之前,我们先来了解一下 Maven 的依赖调解。
依赖调解
参考 Maven依赖冲突问题原理简析 。
简单的说,如果一个项目中引入了同一个依赖(jar 包)的两个(或多个)不同版本,Maven 会对这些依赖做调解,以确保最终(运行时)在项目中对于同一个依赖只引入一个版本。
依赖调解的两个原则:
- 路径最近者优先
- 第一声明者优先
依赖冲突
现在我们已经知道了,在一个项目中是可能引入同一个依赖(jar 包)的不同版本的。 那么,在同一个 jar 包的不同版本中可能存在一些差异,比如:
- 类数量变化,表现形式为类的增加/减少
- 类结构变化,表现形式为方法/字段的增加/减少
- ......
举个例子,我们项目中引入了两个版本(1.0、2.0)的依赖 A,经过依赖调解后使用了较高的版本(2.0)。如果该依赖在升级的时候没有保持向下的兼容性,删掉了某些方法,而我们项目中使用 1.0 版本的地方刚好调用了被删掉的这个方法,就会出现 NoSuchMethodException 异常。
存在依赖冲突一定意味着异常吗?
不一定。如果经过依赖调解后最终使用的版本能够兼容所有的使用,程序就可以正常运行。
为什么会产生依赖冲突
根本原因是通过直接或者间接依赖,在代码中引入了同一个依赖的不同版本。
怎么解决依赖冲突
- 如果能确认高版本向下兼容,那么引入一个高版本 jar 包作为项目的直接依赖(利用路径最近者优先原则)
- 用 标签排除掉不需要的依赖。
怎样更快地发现依赖冲突?
在 IDE 中使用 Maven Enforcer Plugin 插件。
这个插件有很多规则,我们最关心的是 Dependency Convergence规则。
用法:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.4.1</version>
<configuration>
<rules><dependencyConvergence/></rules>
</configuration>
</plugin>
</plugins>
参考官网介绍,该插件有两个执行目标:
- enforcer:enforce 用于规则检查
- enforcer:display-info 用于显示基本环境信息
执行方式:
- 命令行执行
mvn enforcer:enforce - 将该目标绑定到 Maven 的生命周期上自动执行
执行结果:
Dependency convergence error for log4j:log4j:1.2.17 paths to dependency are:
+-com.ricston.conflict:conflict-info:2.1.3-SNAPSHOT
+-org.slf4j:slf4j-log4j12:1.7.6
+-log4j:log4j:1.2.17
and
+-com.ricston.conflict:conflict-info:2.1.3-SNAPSHOT
+-log4j:log4j:1.2.16
根据这种方式发现依赖冲突后,我们就可以使用上面提到的两种方式(排除或者引入可用的直接依赖)来解决冲突。