解决maven依赖冲突

一、前盐

什么是依赖冲突

依赖冲突是指项目依赖的某一个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 -> B -> C -> <span style="color:#a6e22e">X</span>(<span style="color:#ae81ff">1.0</span>)
依赖链路二:F -> D -> <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 -> B -> <span style="color:#a6e22e">Y</span>(<span style="color:#ae81ff">1.0</span>)
c -> D -> <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中的依赖。

三、如何排除依赖

我们先来解释下什么是传递性依赖

1、什么是传递性依赖

比如当我们项目中,引用了A的依赖,A的依赖通常又会引入B的jar包,B可能还会引入C的jar包。

这样,当你在pom.xml文件中添加了A的依赖,Maven会自动的帮你把所有相关的依赖都添加进来。

这里随便举一个例子,比如我们为了实现导入导出功能我们可能会引入poi

<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-xml">  <span style="color:#f8f8f2"><<span style="color:#f92672 !important">dependency</span>></span>
     <span style="color:#f8f8f2"><<span style="color:#f92672 !important">groupId</span>></span>org.apache.poi<span style="color:#f8f8f2"></<span style="color:#f92672 !important">groupId</span>></span>
     <span style="color:#f8f8f2"><<span style="color:#f92672 !important">artifactId</span>></span>poi-ooxml<span style="color:#f8f8f2"></<span style="color:#f92672 !important">artifactId</span>></span>
     <span style="color:#f8f8f2"><<span style="color:#f92672 !important">version</span>></span>3.10.1<span style="color:#f8f8f2"></<span style="color:#f92672 !important">version</span>></span>
  <span style="color:#f8f8f2"></<span style="color:#f92672 !important">dependency</span>></span>
</code></span></span>

当你引入它的时候,它其实还会映入其它jar包

<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-xml">  <span style="color:#f8f8f2"><<span style="color:#f92672 !important">dependencies</span>></span>
    <span style="color:#f8f8f2"><<span style="color:#f92672 !important">dependency</span>></span>
       <span style="color:#f8f8f2"><<span style="color:#f92672 !important">groupId</span>></span>org.apache.poi<span style="color:#f8f8f2"></<span style="color:#f92672 !important">groupId</span>></span>
       <span style="color:#f8f8f2"><<span style="color:#f92672 !important">artifactId</span>></span>poi<span style="color:#f8f8f2"></<span style="color:#f92672 !important">artifactId</span>></span>
       <span style="color:#f8f8f2"><<span style="color:#f92672 !important">version</span>></span>3.10.1<span style="color:#f8f8f2"></<span style="color:#f92672 !important">version</span>></span>
    <span style="color:#f8f8f2"></<span style="color:#f92672 !important">dependency</span>></span>
    <span style="color:#f8f8f2"><<span style="color:#f92672 !important">dependency</span>></span>
      <span style="color:#f8f8f2"><<span style="color:#f92672 !important">groupId</span>></span>org.apache.poi<span style="color:#f8f8f2"></<span style="color:#f92672 !important">groupId</span>></span>
      <span style="color:#f8f8f2"><<span style="color:#f92672 !important">artifactId</span>></span>poi-ooxml-schemas<span style="color:#f8f8f2"></<span style="color:#f92672 !important">artifactId</span>></span>
      <span style="color:#f8f8f2"><<span style="color:#f92672 !important">version</span>></span>3.10.1<span style="color:#f8f8f2"></<span style="color:#f92672 !important">version</span>></span>
    <span style="color:#f8f8f2"></<span style="color:#f92672 !important">dependency</span>></span>
    <span style="color:#f8f8f2"><<span style="color:#f92672 !important">dependency</span>></span>
        <span style="color:#f8f8f2"><<span style="color:#f92672 !important">groupId</span>></span>dom4j<span style="color:#f8f8f2"></<span style="color:#f92672 !important">groupId</span>></span>
        <span style="color:#f8f8f2"><<span style="color:#f92672 !important">artifactId</span>></span>dom4j<span style="color:#f8f8f2"></<span style="color:#f92672 !important">artifactId</span>></span>
        <span style="color:#f8f8f2"><<span style="color:#f92672 !important">version</span>></span>1.6.1<span style="color:#f8f8f2"></<span style="color:#f92672 !important">version</span>></span>
    <span style="color:#f8f8f2"></<span style="color:#f92672 !important">dependency</span>></span>
 <span style="color:#f8f8f2"></<span style="color:#f92672 !important">dependencies</span>></span>
</code></span></span>

就这样一层层的,Maven会自动的帮你把所有相关的依赖都添加进来。传递性依赖会给项目引入很多依赖,简化项目依赖管理,但是也会带来问题。

最明显的就是容易发生依赖冲突。

2、如何排除依赖

关键字:exclusions

exclusions可以包含一个或者多exclusion子元素,因此可以排除一个或者多个传递性依赖。

需要注意的是,声明exclusion的时候只需要groupldartifactld,而不需要要version元素。

示例

<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-xml">   <span style="color:#f8f8f2"><<span style="color:#f92672 !important">dependency</span>></span>
        <span style="color:#f8f8f2"><<span style="color:#f92672 !important">groupId</span>></span>org.apache.poi<span style="color:#f8f8f2"></<span style="color:#f92672 !important">groupId</span>></span>
        <span style="color:#f8f8f2"><<span style="color:#f92672 !important">artifactId</span>></span>poi-ooxml<span style="color:#f8f8f2"></<span style="color:#f92672 !important">artifactId</span>></span>
        <span style="color:#f8f8f2"><<span style="color:#f92672 !important">version</span>></span>3.10.1<span style="color:#f8f8f2"></<span style="color:#f92672 !important">version</span>></span>
            <span style="color:#75715e"><!--排除poi依赖--></span>
        <span style="color:#f8f8f2"><<span style="color:#f92672 !important">exclusions</span>></span>
            <span style="color:#f8f8f2"><<span style="color:#f92672 !important">exclusion</span>></span>
                <span style="color:#f8f8f2"><<span style="color:#f92672 !important">artifactId</span>></span>poi<span style="color:#f8f8f2"></<span style="color:#f92672 !important">artifactId</span>></span>
                <span style="color:#f8f8f2"><<span style="color:#f92672 !important">groupId</span>></span>org.apache.poi<span style="color:#f8f8f2"></<span style="color:#f92672 !important">groupId</span>></span>
             <span style="color:#f8f8f2"></<span style="color:#f92672 !important">exclusion</span>></span>
        <span style="color:#f8f8f2"></<span style="color:#f92672 !important">exclusions</span>></span>
   <span style="color:#f8f8f2"></<span style="color:#f92672 !important">dependency</span>></span>
</code></span></span>

四、实例演示如何解决依赖冲突

我们先来复现因为依赖冲突导致项目报错的。

1、制造依赖冲突

在当前项目中,我即引用了org.apache.poi,又引用了com.alibaba的easyexcel。

pom文件如下:

<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-xml">   <span style="color:#f8f8f2"><<span style="color:#f92672 !important">dependency</span>></span>
        <span style="color:#f8f8f2"><<span style="color:#f92672 !important">groupId</span>></span>org.apache.poi<span style="color:#f8f8f2"></<span style="color:#f92672 !important">groupId</span>></span>
        <span style="color:#f8f8f2"><<span style="color:#f92672 !important">artifactId</span>></span>poi-ooxml<span style="color:#f8f8f2"></<span style="color:#f92672 !important">artifactId</span>></span>
        <span style="color:#f8f8f2"><<span style="color:#f92672 !important">version</span>></span>3.10.1<span style="color:#f8f8f2"></<span style="color:#f92672 !important">version</span>></span>
   <span style="color:#f8f8f2"></<span style="color:#f92672 !important">dependency</span>></span>
   <span style="color:#f8f8f2"><<span style="color:#f92672 !important">dependency</span>></span>
        <span style="color:#f8f8f2"><<span style="color:#f92672 !important">groupId</span>></span>com.alibaba<span style="color:#f8f8f2"></<span style="color:#f92672 !important">groupId</span>></span>
        <span style="color:#f8f8f2"><<span style="color:#f92672 !important">artifactId</span>></span>easyexcel<span style="color:#f8f8f2"></<span style="color:#f92672 !important">artifactId</span>></span>
        <span style="color:#f8f8f2"><<span style="color:#f92672 !important">version</span>></span>2.2.8<span style="color:#f8f8f2"></<span style="color:#f92672 !important">version</span>></span>
   <span style="color:#f8f8f2"></<span style="color:#f92672 !important">dependency</span>></span>
</code></span></span>

然后在项目中添加如下代码:

<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@SpringBootApplication</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">MainApplication</span> {

	<span style="color:#f92672">public</span> <span style="color:#f92672">static</span> <span style="color:#f92672">void</span> <span style="color:#a6e22e">main</span><span style="color:#f8f8f2">(String[] args)</span> {
		SpringApplication.run(MainApplication.class, args);
		test();

	}
	<span style="color:#f92672">public</span> <span style="color:#f92672">static</span> <span style="color:#f92672">void</span> <span style="color:#a6e22e">test</span><span style="color:#f8f8f2">()</span> {
		<span style="color:#75715e">//1.获取文件地址</span>
		<span style="color:#e6db74">String</span> <span style="color:#e6db74">fileName</span> <span style="color:#ab5656">=</span> <span style="color:#e6db74">"/excel/test.xlsx"</span>;
		<span style="color:#75715e">//2、调用easyExcel里面的方法实现写操作</span>
		EasyExcel.write(fileName, UserDto.class).sheet(<span style="color:#e6db74">"某某报表"</span>).doWrite(<span style="color:#f92672">new</span> <span style="color:#a6e22e">ArrayList</span><>());
	}
}	
</code></span></span>

我们发现在SpringBoot启动的时候就报NoClassDefFoundError异常了。

这就是依赖冲突导致的异常报错。

为什么会这样呢,我们可以通过idea看下,打开maven菜单,点击show dependencies

看到这里你大概就已经明白,因为我当前的easyexcel引入的poi是3.17版本的,但是因为根据maven依赖原则,实际引入的poi版本确实是3.10.1版本的。

而DefaultTempFileCreationStrategy这个类在3.10.1这个版本并没有,只有3.17版本才有,所以报了这个错误,这么一解释是不是都通了。

然后我们再来思考一个问题,上面这个案例我们一眼就知道是最终应用哪个依赖里的哪个版本,但如果你的项目中依赖许许多多的jar,肉眼排查就没那么方便了,这里推荐一个

Maven管理插件

2、Maven Helper插件分析jar包冲突

选择并下载插件

在pom文件中看到 dependency analyzer标志,说明maven helper插件就安装成功了。

点击dependency analyzer之后就会进入到下面的页面

从图中可以看出有哪些jar存在冲突,存在冲突的情况下最终采用了哪个依赖的版本。标红的就是冲突版本,白色的是当前的解析版本

如果我们想保留标红的版本,那我们可以标白区域右击选择排除(Exclude)

然后我们再来看pom文件,发现在org.apache.poi中已经移除了poi了。

<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-xml">   <span style="color:#f8f8f2"><<span style="color:#f92672 !important">dependency</span>></span>
       <span style="color:#f8f8f2"><<span style="color:#f92672 !important">groupId</span>></span>org.apache.poi<span style="color:#f8f8f2"></<span style="color:#f92672 !important">groupId</span>></span>
       <span style="color:#f8f8f2"><<span style="color:#f92672 !important">artifactId</span>></span>poi-ooxml<span style="color:#f8f8f2"></<span style="color:#f92672 !important">artifactId</span>></span>
       <span style="color:#f8f8f2"><<span style="color:#f92672 !important">version</span>></span>3.10.1<span style="color:#f8f8f2"></<span style="color:#f92672 !important">version</span>></span>
       <span style="color:#f8f8f2"><<span style="color:#f92672 !important">exclusions</span>></span>
          <span style="color:#f8f8f2"><<span style="color:#f92672 !important">exclusion</span>></span>
             <span style="color:#f8f8f2"><<span style="color:#f92672 !important">artifactId</span>></span>poi<span style="color:#f8f8f2"></<span style="color:#f92672 !important">artifactId</span>></span>
             <span style="color:#f8f8f2"><<span style="color:#f92672 !important">groupId</span>></span>org.apache.poi<span style="color:#f8f8f2"></<span style="color:#f92672 !important">groupId</span>></span>
           <span style="color:#f8f8f2"></<span style="color:#f92672 !important">exclusion</span>></span>
       <span style="color:#f8f8f2"></<span style="color:#f92672 !important">exclusions</span>></span>
    <span style="color:#f8f8f2"></<span style="color:#f92672 !important">dependency</span>></span>
    <span style="color:#f8f8f2"><<span style="color:#f92672 !important">dependency</span>></span>
       <span style="color:#f8f8f2"><<span style="color:#f92672 !important">groupId</span>></span>com.alibaba<span style="color:#f8f8f2"></<span style="color:#f92672 !important">groupId</span>></span>
       <span style="color:#f8f8f2"><<span style="color:#f92672 !important">artifactId</span>></span>easyexcel<span style="color:#f8f8f2"></<span style="color:#f92672 !important">artifactId</span>></span>
       <span style="color:#f8f8f2"><<span style="color:#f92672 !important">version</span>></span>2.2.6<span style="color:#f8f8f2"></<span style="color:#f92672 !important">version</span>></span>
    <span style="color:#f8f8f2"></<span style="color:#f92672 !important">dependency</span>></span>
</code></span></span>

我们再来看最终引用的poi版本是哪个?

我们发现最终的版本最终已经变成3.17版本,现在再启动就不会报错了,成功解决依赖冲突问题。

五、总结

一般我们在解决依赖冲突的时候,都会选择保留jar高的版本,因为大部分jar在升级的时候都会做到向下兼容,所以只要保留高的版本就不会有什么问题。

但是有些包,版本变化大没法去做向下兼容,高版本删了低版本的某些类或者某些方法,那么这个时候就不能一股脑的去选择高版本,但也不能选择低版本。

就比如下面这个案例

<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-rust">依赖链路一:A -> B -> C -> <span style="color:#a6e22e">X</span>(<span style="color:#ae81ff">1.0</span>)
依赖链路二:F -> D -> <span style="color:#a6e22e">X</span>(<span style="color:#ae81ff">2.0</span>)
</code></span></span>

X(2.0) 没有对 X(1.0) 做向下兼容也就是说可能存在排除哪个都不行,那怎么办我们只能考虑升级A的版本或者降低F的版本。比如A升级到A(2.0),使它依赖的X版本

变成X(2.0)这样的话就解决依赖冲突。

但话有说回来 A升级到A(2.0) 可能会影响许许多多的地方,比如自己项目中代码是否需要改变,或者因为 A升级到A(2.0) 导致 B和C的版本有所改变,这些影响点都需要我

们去考虑的。所以说为什么说一个大型项目稳定后,pom文件的升级是件繁琐的事情,那是因为考虑的东西是在太多了,稍有不慎就会因为依赖冲突而导致系统报错。

到此结束!!!

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值