maven依赖选择策略(依赖调解)

news/2024/11/8 17:32:45/

这里先抛出结论

  • 最短路径原则: 不同级依赖, 选择路径最短(对于传递性依赖和一级依赖)
  • 声明优先原则 : 同级依赖,先声明的覆盖后声明的(对于传递性依赖)
  • 同级依赖后加载覆盖先加载原则(不属于传递性依赖的情况,就是pom里的一级依赖)

我们都知道maven具有传递性依赖的机制,就是当你在pom中引用依赖A时,如果这个依赖A同时依赖B的话,那么maven会自动引入B的依赖,以传递性依赖的形式引入到项目中。

虽然这样做很方便,但是有时也会导致一些问题的产生,比如依赖冲突。
A -> C -> X(1.0)
B -> D -> X(2.0)
如果你在项目中引入了A和B,那么最终哪个依赖会被解析呢?按照上面写出的结论,其实是X(1.0)会被解析,你可能会想,既然maven已经按照规则帮我们选定了依赖,为什么会出现依赖冲突的情况呢?我们举个例子

1、你想如果依赖B的代码中引用了X(2.0)的新创建的类,但因为最终被解析的是X(1.0),所以运行时就会出现很典型的NoClassDefFoundError或ClassNotFoundException依赖冲突报错。
2、如果依赖B的代码中引用X(2.0)的新创建的方法,但因为最终被解析的是X(1.0),所以就会抛出 NoSuchMethodError系统异常。

所以还是得具体情况具体分析,如果我们手动排除依赖X(1.0),引入X(2.0)就不会出现问题了吗?也不一定的,有可能2.0版本的依赖删去了1.0版本中的一些方法和类,这种情况下同样会导致这些问题。

如何知道传递性依赖究竟是从哪条依赖中被引入的?

我们可以通过mvn dependency:tree命令来进行查看,如下图,我在pom文件中引入了两个版本的easyExcel的依赖,由于它们属于一级依赖,按照规则,应该引入后加载的版本
在这里插入图片描述
打开pom.xml所在文件夹,打开cmd命令行,输入mvn dependency:tree,我们可以从图片中看到,引入的确实是2.1.6版本的依赖
在这里插入图片描述

这里再举几个例子,我们进入2.1.6版本的easyexcel依赖中可以看到,该依赖还依赖于slf4j-api-1.7.26的依赖,这里我从中央仓库拿了一份其他版本的slf4j的依赖进行引入,我们刷新pom文件后再看看最终引入的slf4j究竟是什么版本的
重新引入其他版本的slf4j
其实由于上面的结论我们已经知道,最终引入的是1.7.30版本的slf4j-api,因为它们属于不同级的依赖,maven会按照最短路径进行引入
在这里插入图片描述
这里我抛出一个问题,如果slf4j-api-1.7.30版本的依赖比slf4j-api-1.7.26多出了一些新方法和类(这里只是举个例子,假设),并且在项目中我不是直接引入的slf4j-api-1.7.30版本,而是通过传递性依赖的方式引入,比如我引入了A依赖,A->依赖于slf4j-api-1.7.30版本,并且A中的代码引用了slf4j-api-1.7.30版本的新方法和类,如下图所示,这种情况下maven根据- 声明优先原则 : 同级依赖,先声明的覆盖后声明的(对于传递性依赖)会引入slf4j-api-1.7.26版本的依赖,那么问题就来了,当项目运行起来时,A依赖中的代码执行时就会报错了,因为它找不到某些方法和类,这就是典型的依赖冲突!
在这里插入图片描述
这种情况下我们可以通过排除掉easyExcel中的依赖来让maven选择使用A的子依赖,就像下面这样

    <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.1.6</version><exclusions><exclusion><!--手动排除--><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId></exclusion></exclusions></dependency>

这样就可以正确的引入slf4j-api-1.7.30了!

tips

可能有的小伙伴会问,编译器在编译代码的时候不会检查我们引入的依赖的的代码中的方法和类是否存在的吗?
其实是不会的,因为那是已经编译好的class字节码文件,所以依赖冲突一般只在运行时产生。
GPT回答如下:(问题:Java编译代码的时候只会编译我们写的代码吗?)
是的,Java编译器在编译Java源代码时,只会编译我们自己编写的Java类。它并不会自动编译我们所依赖的的第三方库,因为这些库已经编译好了,生成了对应的.class字节码文件。
当我们在Java源代码中调用依赖库中的类或方法时,编译器会检查这些依赖库中所引用的类是否存在,如果存在,则会将它们的字节码文件打包到生成的class文件中,一并输出到编译后的目标代码中。
但如果我们修改了依赖库的源代码,那么需要重新对依赖库进行编译生成新的.class文件,然后再对主程序进行编译和运行。


http://www.ppmy.cn/news/99796.html

相关文章

spring(事务管理)

事物可以看做是由对数据库若干操作组成的一个单元 事务的作用就是为了保证用户的每一个操作都是可靠的&#xff0c;事务中的每一步操作都 必须成功执行&#xff0c;只要有发生异常就回退到事务开始未进行操作的状态,这些操作 要么都完成&#xff0c;要么都取消&#xff0c;从而…

7-10 算术入门之加减乘除

对于输入的两个整数&#xff0c;按照要求输出其和差积商。 输入格式: 在一行内输入两个不超过100的非负整数a和b&#xff0c;中间以一个空格间隔&#xff0c;且保证b不为0。 输出格式: 共四行&#xff0c;格式是&#xff1a; [a] [b] [ab] [a] - [b] [a-b] [a] * [b] [a…

Day4 计算糖果、进制转换

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; C/C相关题解 &#x1f383;操作环境&#xff1a; Visual Studio 2019 版本 16.11.17 文章目录 选择题1、C函数 编程题1、计算糖果2、进制转换 选择题 1、C函数 题目&#xff1a;下列程序执行后,输出的结果为…

Linux :: 【基础指令篇 :: 文件及目录操作:(4)】:: mkdir :: 创建目录:指定路径单个目录创建及一次性创建多级目录

前言&#xff1a;本篇是 Linux 基本操作篇章的内容&#xff01; 笔者使用的环境是基于腾讯云服务器&#xff1a;CentOS 7.6 64bit。 学习集&#xff1a; C 入门到入土&#xff01;&#xff01;&#xff01;学习合集Linux 从命令到网络再到内核&#xff01;学习合集 目录索引&am…

深度学习进阶篇-国内预训练模型[5]:ERINE、ERNIE 3.0、ERNIE-的设计思路、模型结构、应用场景等详解

【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化算法、卷积模型、序列模型、预训练模型、对抗神经网络等 专栏详细介绍&#xff1a;【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化…

如何安装389目录服务器作为CentOS 8 / RHEL 8机器的LDAP服务器?

LDAP&#xff08;轻量级目录访问协议&#xff09;是一种用于访问和维护分布式目录服务的开放标准协议。 389目录服务器是一个功能强大、高性能的LDAP服务器&#xff0c;它可以用于存储和管理用户、组和其他网络对象的身份验证和授权信息。本文将详细介绍如何在CentOS 8 / RHEL…

尝试在UNet添加SK模块和CBAM模块

数据集&#xff1a;refuge数据集 训练轮数&#xff1a;10 Architecture dice coefficientmean IOUunet0.94652.6sk-unet0.98966.1cbam-unet0.98865.8 &#xff08;1&#xff09;在UNet最后的输出卷积前添加&#xff0c;SK模块 训练结果&#xff1a; [epoch: 9] train_loss:…

【LeetCode热题100】打卡第5天:最长回文子串

文章目录 最长回文子串⛅前言&#x1f512;题目&#x1f511;题解 最长回文子串 ⛅前言 大家好&#xff0c;我是知识汲取者&#xff0c;欢迎来到我的LeetCode热题100刷题专栏&#xff01; 精选 100 道力扣&#xff08;LeetCode&#xff09;上最热门的题目&#xff0c;适合初识…