在做工程项目的时候,出于解耦等方面的考虑,我们会吧一个大工程分解成许多的子工程。然后在一个工程中通过添加依赖的方式来引用其他的工程。在依赖关系及其复杂的情况下,就容易产生依赖冲突。
依赖冲突(包冲突)
表现形式
通常的表现形式为抛出异常如下异常
- 1.java.lang.NoSuchMethodError
- 2.java.lang.ClassNotFoundException
- 3.java.lang.NoClassDefFoundError
所以当出现上述异常但是项目编译却能通过的时候我们就要考虑是不是出现了依赖冲突。
产生原因
简单来说就是 A项目中引用了B项目与C项目,C项目中引用了D项目,B项目与D项目中引用了E项目,但是区别是B项目中引用了E项目的1.0.0版本,D项目引用了E项目的1.0.1版本,即A–>B–>E(1.0.1),A–>C–>D–>E(1.0.1),且E项目的1.0.0版本中只有一个叫做One的类,而E项目的1.0.1中有One和OneOne两个类。
放一张上述依赖关系的简单描述图
这样子,A项目中看起来是会依赖两个E包的
我们可以通过下两个名利来检查依赖项目中的依赖关系
mvn -U dependency:tree -Dverbose
完整的依赖关系图,且会告诉你哪个包产生了冲突
mvn -U dependency:tree
项目中实际的依赖关系图。
但是当我我们用 mvn -U dependency:tree去检查包依赖关系时,发现依赖树中显示A项目只依赖了E的1.0.0版本。
这是因为maven自己的依赖处理规则,多个依赖的情况下,遵循先声明先依赖(同深度场景下,先声明的先依赖,简单来说就是哪个写在上边,哪个先依赖),或者是最短路径优先依赖的规则。
上述场景下,因为E1.0.0的依赖路径更短一些,所以maven自己会帮我们去除E1.0.1的依赖版本,选择依赖E1.0.0的版本。
导致结果
因为引用了E的1.0.0版本,所以在整个A的依赖体系下是没有OneOne这个类的,当A项目,C项目或者是D项目中的某个方法需要用到OneOne这个类的话,就会提示你找不到这个类。因为这个类就是没有被依赖进来的。
解决方法
方法一
强制去除B项目中的E1.0.0版本依赖(因为需要使用的是E的1.0.1版本依赖)
<exclusions>
<exclusion>
<groupId>XXXX</groupId>
<artifactId>ModuleE</artifactId>
</exclusion>
</exclusions>
方法二
在主工程的pom文件下制定你想要的版本,这个在最短路径有限依赖的原则下,A项目就会依赖你制定的E版本
<dependency>
<groupId>XXXX</groupId>
<artifactId>moduleE</artifactId>
<version>1.0.1</version>
</dependency>
附测试开源小项目
https://github.com/ctywzy/mavenConflict.git