一、前言
什么是依赖冲突
依赖冲突是指项目依赖的某一个jar包,有多个不同的版本,因而造成了包版本冲突。
依赖冲突的原因
我们在maven项目的pom中 一般会引用许许多多的dependency
。例如,项目A有这样的依赖关系:
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-xml">A -> C -> X(1.0)
B -> D -> X(2.0)
</code></span></span>
X是A的传递性依赖
,但是两条依赖路径上有两个版本的X,那么哪个X会被Maven解析使用呢? 两个版本都被解析显然是不对的,因为那会造成依赖重复,因此必须选择一个。
至于怎么选肯定有它的规则(下面会讲),这里我们先假设最终引用的X(1.0)
版本,这样会不会有问题呢?
当然会有的
1、你想如果B引用X(2.0)的新创建的类,但因为最终被解析的是X(1.0),所以就会出现很典型的NoClassDefFoundError
或ClassNotFoundException
依赖冲突报错。
2、如果B引用X(2.0)的新创建的方法,但因为最终被解析的是X(1.0),所以就会抛出 NoSuchMethodError
系统异常。
但换种角度,如果最终解析的是X(2.0),就没问题了吗?
那也不一定
1、如果X(2.0)删掉了X(1.0)的一些类,但A已经引用了,同样也会报NoClassDefFoundError或者ClassNotFoundException错误。
2、如果X(2.0)删掉了X(1.0)的一些方法,但A已经引用了,同样也会报NoSuchMethodError错误。
所以说具体问题还需具体分析,到底采用哪个版本还需要看实际项目。也可能我们需要升级对应的A或者B的版本才能解决问题。
如果有两个版本的X,Maven是依据什么原则来选择解析哪个X呢?
二、maven依赖原则
maven依赖主要有两大原则
1、路径最近者优先
相同jar不同版本,根据依赖的路径长短来决定引入哪个依赖。
举例
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-rust">依赖链路一:A <span style="color:#444a44">-></span> B <span style="color:#444a44">-></span> C <span style="color:#444a44">-></span> <span style="color:#a6e22e">X</span>(<span style="color:#ae81ff">1.0</span>)
依赖链路二:F <span style="color:#444a44">-></span> D <span style="color:#444a44">-></span> <span style="color:#a6e22e">X</span>(<span style="color:#ae81ff">2.0</span>)
</code></span></span>
该例中X(1.0)的路径长度为3,而X(2.0)的路径长度为2,因此X(2.0)会被解析使用。依赖调解第一原则不能解决所有问题,比如这样的依赖关系:
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-rust">A <span style="color:#444a44">-></span> B <span style="color:#444a44">-></span> <span style="color:#a6e22e">Y</span>(<span style="color:#ae81ff">1.0</span>)
c <span style="color:#444a44">-></span> D <span style="color:#444a44">-></span> <span style="color:#a6e22e">Y</span>(<span style="color:#ae81ff">2.0</span>)
</code></span></span>
Y(1.0)和Y(2.0)的依赖路径长度是一样的,都为2。Maven定义了依赖调解的第二原则:
2、第一声明者优先
在依赖路径长度相等的前提下,在POM中依赖声明的顺序决定了谁会被解析使用,顺序最前的那个依赖优胜。该例中,如果A的依赖声明在C之前,那么Y (1.0)就会被解析使用.
注意
子pom内声明的优先于父pom中的依赖。