解决maven依赖冲突,这篇就够了!
一、前言
什么是依赖冲突
依赖冲突是指项目依赖的某一个jar包,有多个不同的版本,因而造成了包版本冲突。
依赖冲突的原因
我们在maven项目的pom中 一般会引用许许多多的dependency
。例如,项目A有这样的依赖关系:
A -> C -> X(1.0)
B -> D -> X(2.0)
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不同版本,根据依赖的路径长短来决定引入哪个依赖。
举例
依赖链路一:A -> B -> C -> X(1.0)
依赖链路二:F -> D -> X(2.0)
该例中X(1.0)的路径长度为3,而X(2.0)的路径长度为2,因此X(2.0)会被解析使用。依赖调解第一原则不能解决所有问题,比如这样的依赖关系:
A -> B -> Y(1.0)
c -> D -> Y(2.0)
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
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.10.1</version>
</dependency>
当你引入它的时候,它其实还会映入其它jar包
<dependencies>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.10.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.10.1</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
</dependencies>
就这样一层层的,Maven会自动的帮你把所有相关的依赖都添加进来。传递性依赖会给项目引入很多依赖,简化项目依赖管理,但是也会带来问题。
最明显的就是容易发生依赖冲突。
2、如何排除依赖
关键字:exclusions
exclusions可以包含一个或者多exclusion子元素,因此可以排除一个或者多个传递性依赖。
需要注意的是,声明exclusion的时候只需要groupld
和artifactld
,而不需要要version元素。
示例
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.10.1</version>
<!--排除poi依赖-->
<exclusions>
<exclusion>
<artifactId>poi</artifactId>
<groupId>org.apache.poi</groupId>
</exclusion>
</exclusions>
</dependency>
四、实例演示如何解决依赖冲突
我们先来复现因为依赖冲突导致项目报错的。
1、制造依赖冲突
在当前项目中,我即引用了org.apache.poi,又引用了com.alibaba的easyexcel。
pom文件如下:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.10.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.8</version>
</dependency>
然后在项目中添加如下代码:
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
test();
}
public static void test() {
//1.获取文件地址
String fileName = "/excel/test.xlsx";
//2、调用easyExcel里面的方法实现写操作
EasyExcel.write(fileName, UserDto.class).sheet("某某报表").doWrite(new ArrayList<>());
}
}
我们发现在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了。
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.10.1</version>
<exclusions>
<exclusion>
<artifactId>poi</artifactId>
<groupId>org.apache.poi</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.6</version>
</dependency>
我们再来看最终引用的poi版本是哪个?
我们发现最终的版本最终已经变成3.17版本,现在再启动就不会报错了,成功解决依赖冲突问题。
五、总结
一般我们在解决依赖冲突的时候,都会选择保留jar高的版本,因为大部分jar在升级的时候都会做到向下兼容,所以只要保留高的版本就不会有什么问题。
但是有些包,版本变化大没法去做向下兼容,高版本删了低版本的某些类或者某些方法,那么这个时候就不能一股脑的去选择高版本,但也不能选择低版本。
就比如下面这个案例
依赖链路一:A -> B -> C -> X(1.0)
依赖链路二:F -> D -> X(2.0)
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文件的升级是件繁琐的事情,那是因为考虑的东西是在太多了,稍有不慎就会因为依赖冲突而导致系统报错。
声明: 公众号如需转载该篇文章,发表文章的头部一定要 告知是转至公众号: 后端元宇宙
。同时也可以问本人要markdown原稿和原图片。其它情况一律禁止转载!
解决maven依赖冲突,这篇就够了!的更多相关文章
- 解决maven依赖冲突问题
解决maven依赖冲突问题 1.idea 安装maven helper插件 2.打开pom.xml文件 底部多出一个Dependency Analyzer选项 点开这个选项 找到冲突,点击右键,选择E ...
- IDEA 解决 Maven 依赖冲突的高能神器,这一篇够不够?
1.何为依赖冲突 Maven是个很好用的依赖管理工具,但是再好的东西也不是完美的.Maven的依赖机制会导致Jar包的冲突.举个例子,现在你的项目中,使用了两个Jar包,分别是A和B.现在A需要依 ...
- 如何快速的解决Maven依赖冲突
为什么会出现依赖冲突 首先要说明Maven的依赖管理,具体的可以参考这边 Maven学习——依赖管理 这篇文章,maven在依赖冲管理中有一下几个原则. 依赖是使用Maven坐标来定位的,而Maven ...
- Java-idea-常用技巧-转maven,解决包依赖冲突
1.Intellij IDEA如何将普通工程转换成maven工程 项目上右键 Add Framework Support,选择maven 2.Intellij IDEA 自动生成 serialVers ...
- 【核心】project(idea文件)、module(iml文件)到SSM集成、热部署、Tomcat启动、MAVEN依赖冲突
http://wiki.jikexueyuan.com/project/intellij-idea-tutorial/project-composition-introduce.html 在 Inte ...
- Android Studio 解决 Gradle 依赖冲突的问题
Android Studio 解决 Gradle 依赖冲突的问题 参考链接: Android Studio(Gradle)解决库依赖冲突问题:http://www.mobibrw.com/2016/3 ...
- 说说maven依赖冲突,依赖调解,依赖传递和依赖范围
说maven依赖冲突之前需要先说说maven的 依赖传递. 依赖传递 当前项目引入了一个依赖,该依赖的依赖也会被引入项目.更加准确的说法是,maven会解析直接依赖的POM,将那些必要的间接依赖,以传 ...
- 快速发现并解决maven依赖传递冲突
此文已由作者翟曜授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 最近在测试过程中,遇到了几次maven传递依赖冲突的问题,所以记录下解决的过程,遇到类似问题供参照. 问题现象 ...
- 解决Maven依赖jar包冲突总结
maven导入jar包中的一些概念: 直接依赖:项目中直接导入的jar包,就是该项目的直接依赖包. 传递依赖:项目中没有直接导入的jar包,可以通过项目直接依赖jar包传递到项目中 ...
随机推荐
- Python入门-异常处理
异常处理 #try----else---- 会一起执行 #finally无论如何,最后都会执行 def main(): try: res = 10/2 print("开始执行计算:" ...
- ubuntu创建pycharm快捷方式或不显示图标
ubuntu创建pycharm快捷方式或不显示图标 删除之前残留的pycharm快捷方式文件. sudo rm /usr/share/applications/jetbrains-pycahrm.de ...
- 服务器的cpu 核心、线程
此版本有大范围改动,因为cpu作为一个大脑,所以更细致的进行了,相关的分析和阐述. 1.版本1. 2022.1.242.版本2: 2022.3.2 采集数据: ht2机器为物理机,cpu是4颗cpu, ...
- Typora基本使用语法(超好用的代码编辑工具)
Typora代码编辑软件,一款适合新手小白的做笔记工具,操作简单,大家可以去试试......
- 基于kubernetes的分布式限流
做为一个数据上报系统,随着接入量越来越大,由于 API 接口无法控制调用方的行为,因此当遇到瞬时请求量激增时,会导致接口占用过多服务器资源,使得其他请求响应速度降低或是超时,更有甚者可能导致服务器宕机 ...
- 以ARM和RISC-V为内核的单片机写寄存器
我以为这是个很简单的问题,没想到还有一些初学者不会.可能他们也是跟我一样是直接学的如何操作单片机并没有学微机原理么. ARM和RISC-V的机器的系统架构都是哈佛结构的,意思是程序存储器.数据存储器和 ...
- Java实现平滑加权轮询算法--降权和提权
上一篇讲了普通轮询.加权轮询的两种实现方式,重点讲了平滑加权轮询算法,并在文末留下了悬念:节点出现分配失败时降低有效权重值:成功时提高有效权重值(但不能大于weight值). 本文在平滑加权轮询算法的 ...
- selenium模块获得js动态数据-17track为例
通过selenium模块驱动Chrome浏览器,获得js动态数据,以17track为例:通过运单号查询最新的物流信息 1 import re 2 from time import sleep 3 fr ...
- python学习-Day22
目录 今日内容详细 hashlib加密模块 什么是加密 加密算法 加密的使用 基本使用 指定算法(md5) 将明文数据传递给算法对象 获取加密之后的密文数据 加密补充 加盐处理 动态加盐 加密应用场景 ...
- 攻防世界-MISC:stegano
这是攻防世界新手练习区的第五题,题目如下: 点击附件1下载,得到一个pdf文件,打开后内容如下: 把pdf文件里的内容复制到记事本上,发现一串A和B的字符串,不知道是什么(真让人头大) 参考一下WP, ...