【Maven】依赖冲突如何解决?

准备工作

1、创建一个空工程 maven_dependency_conflict_demo,在 maven_dependency_conflict_demo 创建不同的 Maven 工程模块,用于演示本文的一些点。
请添加图片描述

什么是依赖冲突?

  • 引入同一个依赖的多个不同版本时,就会发生依赖冲突。

演示 1

步骤:

1、在 maven_dependency_conflict_demo 中创建 2 个基于 Maven 的 JavaSE 项目 maven_00 和 maven_01。
2、在 maven_01 的 pom.xml 文件中引入 junit 4.12 版本。
3、将 maven_01 打包安装到私服。
4、在 maven_00 的 pom.xml 文件中引入 junit 4.13 版本和 maven_01。

结果如下图所示,可以看到,的的确确发生了依赖冲突,而且 Maven 自己处理了依赖冲突。Maven 处理冲突依据的是短路径优先原则,后面会提到。
请添加图片描述

Maven的依赖调解原则

Maven 在遇到依赖冲突时,会先根据路径最短优先声明顺序优先两大原则处理冲突。

1、短路径优先

当一个间接依赖存在多条引入路径时,引入路径短的会被解析使用。
见演示 1,依赖关系图如下图所示:
请添加图片描述

附:代码分支 2411291-larryla-maven-name-first。

2、声明顺序优先

如果存在短路径,则优先选择短路径。如果路径相同,则先声明者优先。

演示 2

步骤:

1、在 maven_dependency_conflict_demo 中创建 3 个基于 Maven 的 JavaSE 项目 maven_02、maven_03 和 maven_04。
2、在 maven_03 中引入 junit 4.12 版本。
3、将 maven_03 打包安装到私服。
4、在 maven_04 中引入 junit 4.13 版本。
5、将 maven_04 打包安装到私服。
6、在 maven_02 中引入 maven_03 和 maven_04,查看 junit 版本引入情况。

上面步骤对应的依赖关系图如下:
请添加图片描述
结果如下:

(1) 若 maven_03 在 maven_04 前面,则 maven_02 中引入的是 junit 4.12
请添加图片描述
(2) 若 maven_04 在 maven_03 前面,则 maven_02 中引入的是 junit 4.13
请添加图片描述

附:代码分支 2411291-larryla-maven-name-first。

3、特殊情况

(1) 子 pom 覆盖父 pom。

如果在父工程和子工程引入了相同依赖的不同版本,那么子工程中的版本会覆盖覆盖父工程中的版本。

步骤:

1、在 maven_dependency_conflict_demo 中创建 2 个基于 Maven 的 JavaSE 项目 maven_07 和 maven_08。
2、在 maven_07 中引入 junit 4.13 版本。
3、在 maven_08 重新指定的 junit 为 4.12 版本。

结果如下图所示,可以看到,子工程引入的是 junit 4.12 版本,而不是继承自父工程的 4.13 版本,这说明子工程覆盖了父工程的版本。
请添加图片描述
(2) 同一个 pom.xml 文件里配置相同资源的不同版本,后配置的覆盖先配置的。

在 maven_07 的 pom.xml 文件中先后引入 MyBatis 3.5.11 和 3.5.7 版本,结果如下图。可以看到,后声明的 3.5.7 版本覆盖了先声明的 3.5.11 版本。
请添加图片描述

排除依赖

情景

如果仅仅依靠 Maven 来进行依赖调解,在很多情况下是不适用的,这时需要我们手动排除依赖。

举个例子,当前项目存在下面的依赖关系:

依赖链路一:A -> B -> C -> X(2.0.0) // dist = 3
依赖链路二:A -> D -> X(1.0.0) // dist = 2

根据路径最短优先原则,X(1.0) 会被解析使用,也就是说 A 中实际用的是 1.0 版本的 X。但是,这样可能会导致错误。

假如 X(2.0) 相较于 X(1.0) 新增了 1 个类和 1 个方法,并且 A 中使用了新增的类和方法,编译不通过。

问题演示

步骤:

1、在 maven_dependency_conflict_demo 中创建 5 个基于 Maven 的 JavaSE 项目 maven_a、maven_b、maven_c 、maven_d 和 maven_x。
2、在 maven_x 的 1.0 版本打包安装到私服的 Release 仓库。
3、将 maven_x 的 2.0 版本打包安装到私服的 Release 仓库。
4、在 maven_c 中引入 maven_x 的 2.0 版本;将 maven_c 打包安装到私服的 Release 仓库。在 maven_b 中引入 maven_c;将 maven_b 打包安装到私服的 Release 仓库。在 maven_a 中引入 maven_b。
5、在 maven_d 中引入 maven_x 的 1.0 版本,将 maven_d 打包安装到私服的 Release 仓库。在 maven_a 中引入 maven_d。

上面步骤对应的依赖关系图如下:
请添加图片描述
maven_x 1.0.0 版本:
请添加图片描述
在 2.0.0 版本中,变化如下:

  • 在 BallSports 类中,新增方法 playFootBall。
  • 新增类 SwimmingSports,类中包含方法 FreeStyle。

结果如下图,依据短路径优先原则,在 maven_a 模块中引入了 maven_x 1.0.0 版本,舍弃了 maven_x 2.0.0 版本,所以无法使用 2.0.0 版本中新增的类和方法
请添加图片描述

解决方案

一般在解决依赖冲突的时候,我们都会优先保留版本较高的,这是因为大部分 jar 在升级的时候都会做到向下兼容。

1、exclusions标签

具体做法是,在 maven_a 的 pom.xml 文件中使用 exclusion 标签显式排除掉 maven_x 1.0.0 版本的依赖。结果如下图所示,成功引入 maven_x 2.0.0 版本,舍弃 1.0.0 版本。
请添加图片描述

2、optional标签

我们还可以使用 optional 标签,对外隐藏当前所依赖的资源。

具体做法是,在 maven_d 中引入 maven_x 1.0.0 版本时指定 optional 标签值为 true。

结果如下图所示,可以看到,使用 optional 标签可以使 maven_x 1.0.0 版本的 jar 包不再通过 maven_d 传递,也就是 maven_a 在引入 maven_b 时,得不到 maven_x 1.0.0 的 jar 包。

请添加图片描述

附:代码分支 2411292-larryla-maven-dependency-exclusion。

对比:标签和标签的区别

由上可知,在 maven_a → maven_d → maven_x 这条分支上,我们有两种解决依赖冲突的方法,即 exclusions 标签和 optional 标签。两种方法的本质都是不将 maven_x 1.0.0 jar 包传递到 maven_a 中。但区别在于:

  • 排除依赖是在 maven_a 引入 maven_d 依赖时,同时设置<exclusions>,maven_a 知道 maven_x 1.0.0 的存在,主动将其排除掉。这种适用于不能修改 maven_d 的配置文件的情况
  • 可选依赖是在 maven_d 上为其依赖项 maven_x 设置<optional>,是 maven_d 不让"外界"知道依赖 maven_x 的存在,不再传递依赖 maven_x,此时,maven_a 不知道 maven_x 1.0.0 的存在。这种适用于可以修改 maven_d 的配置文件的情况

思考 1

在"问题演示"中,我们复现了短路径优先可能导致的问题,即 maven_a 会引入 maven_x 1.0.0 版本,舍弃 maven_x 2.0.0 版本。那么问题来了,既然引入了 maven_x 1.0.0 版本,那么 maven_b 和 maven_c 会用哪个版本的 maven_x 呢?
请添加图片描述
结果如下,由编辑区的方法调用和右侧的依赖管理可以得知,maven_a 为了解决依赖冲突选择引入 maven_x 1.0.0 版本,而 maven_b 和 maven_c 不受 maven_a 的影响,引入的依旧是 maven_x 2.0.0 版本:
请添加图片描述
因此,上面给出的依赖传递图不是很精确的,应该如下:

  • maven_a 引入 maven_x 1.0.0
  • maven_b、maven_c 引入 maven_x 2.0.0
    请添加图片描述

附:代码分支 2411292-larryla-maven-dependency-exclusion。

思考 2

对于"情景"一节,Maven 在依据短路径优先原则调解冲突时可能会带来一些问题。假如 X(2.0) 相较于 X(1.0) 新增了 1 个类和 1 个方法:

  • A 中使用了新增的类,编译不通过。
  • A 中使用了新增的方法,编译不通过。

我们在"问题演示"一节复现了上面两个问题,并且在"解决方案"一节中使用两种方案 exclusions 和 optional,引入了高版本 maven_x 2.0.0 解决了这个问题。

但是,引入高版本就没有问题了吗?

还是下图的依赖关系,假如 maven_a 引入了 maven_x 2.0.0 版本。
请添加图片描述
假如 maven_x 2.0.0 相比于 maven_x 1.0.0 版本删掉了一个类,但 maven_a 已经引用了,这时会报错;maven_x 2.0.0 相比于 maven_x 1.0.0 版本删掉了一个方法,但 maven_a 已经引用了,同样也会报错。

演示

我们重新组织 maven_x 1.0.0 和 maven_x 2.0.0 版本包括的内容。

maven_x 1.0.0 版本内容如下:

  • BallSports:playBasketBall 方法、playFootBall 方法

  • SwimmingSports:FreeStyle 方法
    请添加图片描述
    maven_x 1.0.0 版本内容变化如下:

  • 删掉 BallSports 类中的 playBasketBall 方法

  • 删掉 SwimmingSports 类

步骤:

1、在 maven_x 的 1.0 版本打包安装到私服的 Release 仓库。
2、将 maven_x 的 2.0 版本打包安装到私服的 Release 仓库。
3、在 maven_c 中引入 maven_x 的 2.0 版本;将 maven_c 打包安装到私服的 Release 仓库。在 maven_b 中引入 maven_c;将 maven_b 打包安装到私服的 Release 仓库。在 maven_a 中引入 maven_b。
4、在 maven_d 中引入 maven_x 的 1.0 版本,将 maven_d 打包安装到私服的 Release 仓库。在 maven_a 中引入 maven_d。

结果如下,可以看到 maven_a 中引入的是 maven_x 2.0.0 版本,无法使用 2.0.0 版本相比 1.0.0 版本删掉的方法和类,直接编译不通过。
请添加图片描述

附:代码分支 2411301-larryla-maven-dependency-exclusion-high-ver-problem。

聚合工程,统一管理版本

聚合工程:一个项目允许创建多个子模块,多个子模块组成一个整体,可以统一进行项目的构建。要弄明白聚合工程,得先清楚"父子工程"的概念:

  • 父工程:没有任何代码、仅有pom.xml的空项目,用来定义公共依赖、插件和配置;
  • 子工程:编写具体代码的子项目,可以继承父工程的依赖项、配置,还可以独立拓展。

而 Maven 聚合工程,就是基于父子工程结构,将一个完整项目,划分出不同的层次。这种方式可以很好的管理多模块之间的依赖关系,以及构建顺序,大大提高了开发效率、维护性。

为了防止不同子工程引入不同版本的依赖,在父工程中,统一对依赖的版本进行控制,规定所有子工程都使用同一版本的依赖,可以使用<dependencyManagement>标签来管理。

子工程在使用<dependencyManagement>中已有的依赖项时,不需要写<version>版本号,版本号在父工程中统一管理,这样做的好处在于:以后为项目的技术栈升级版本时,不需要单独修改每个子工程的POM,只需要修改父POM文件即可,减少了版本冲突的可能性。

演示

步骤:

1、在 maven_dependency_conflict_demo 中创建 2 个基于 Maven 的 JavaSE 项目 maven_05 和 maven_06。
2、在 maven_05 中引入 junit 4.13 版本,使用 dependencyManagement 标签统一管理。
3、在 maven_06 继承 maven_05 的 junit 4.13 版本。

上面步骤对应的依赖关系图如下:
请添加图片描述
结果如下图所示,可以看到,在 maven_06 工程中,只需要指定 groupId 和 artifactId,不需要指定 version,就可以从父工程 maven_05 中继承 junit 4.13 依赖。
请添加图片描述

其他

1、子工程继承父工程的依赖时,可以指定版本,这时子工程的版本会覆盖父工程的版本。

演示:

还是使用"演示"一节的模块。在 maven_06 模块中指定 junit 的版本为 4.12,那么 maven_06 引入的 junit 将是 4.12 版本,而不是父工程的 4.13 版本:
请添加图片描述
虽然可以在子工程中指定其他版本,但不建议这么做。如果这样做的话,使用 dependencyManagement 的意义就不大了。

2、父工程可以使用 properties 标签指定依赖的版本,然后在 GAV 坐标中引入该版本。这样在升级依赖版本时,只需要修改 properties 标签中变量的值,而无需修改 GAV 中的 version 标签的值。

演示:

还是使用"演示"一节的模块。在 maven_05 的 pom.xml 文件中,在 properties 标签中自定义一个 junit-version 标签,用于指定 junit 依赖的版本号。然后,在 version 标签中使用 ${junit-version} 的形式声明版本号。如下图所示,maven_06 依旧成功引入了 junit 4.13 版本。
请添加图片描述
3、dependencyManagement 标签只是用于声明依赖,不会在项目中引入依赖。

如下图所示,只是在聚合工程的子工程中引入了 junit 依赖,父工程并未引入依赖。
请添加图片描述
4、dependencyManagement 和 dependencies 的区别

  • <dependencies>:定义强制性依赖,写在该标签里的依赖项,子工程必须继承。
  • <dependencyManagement>:定义可选性依赖,该标签里的依赖项,子工程可选择性地继承。

附:代码分支 2411302-larryla-maven-dependency-uni-manage

排查依赖冲突

在 MySQL 中,我们会借助一些工具进行慢 SQL 分析。那么,在 Maven 中,我们是否可以借助一些工具进行依赖冲突定位呢?

我们可以在插件市场下载"Maven Helper" 插件辅助排查依赖冲突。
请添加图片描述
在"排除依赖-情景"一节中,我们分析了如下依赖冲突情况,得到的结论是 Maven 会依据短路径优先原则引入 X(1.0) 版本。

依赖链路一:A -> B -> C -> X(2.0.0) // dist = 3
依赖链路二:A -> D -> X(1.0.0) // dist = 2

接下来,我们使用 Maven Helper 插件来验证一下。

打开 maven_a 的 pom.xml 文件,在下方点击 Dependency Analyzer,然后点击 Refresh UI,会弹出如下界面:
请添加图片描述
提示有一个依赖冲突,即 maven_x: 1.0.0。我们点开看看,发现与之冲突的是 maven_x 2.0.0。这就和上面呼应上了,Maven 依据短路径优先原则,选择了 1.0.0 版本,舍弃了 2.0.0 版本。
请添加图片描述
在"排除依赖-解决方案"一节中,我们基于排除依赖引入了 2.0.0 版本,这里,我们同样保留 2.0.0 版本。在 1.0.0 上右键,选择 exclude,表示排除掉该版本。然后点击上面的 Reimport,再点击 Refresh UI。
请添加图片描述
返回到 pom.xml 文件,发现点击上面的 exclude 按钮,就是在 pom.xml 文件中使用了 exclusions 标签:
请添加图片描述

思维导图

请添加图片描述
代码仓库:git@github.com:Acura-bit/maven_dependency_conflict_demo.git

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/62710.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Vue3】从零开始创建一个VUE项目

【Vue3】从零开始创建一个VUE项目 手动创建VUE项目附录 package.json文件报错处理: Failed to get response from https://registry.npmjs.org/vue-cli-version-marker 相关链接&#xff1a; 【VUE3】【Naive UI】&#xff1c;NCard&#xff1e; 标签 【VUE3】【Naive UI】&…

Pytorch-GPU版本离线安装

最近在复现一项深度学习的工作&#xff0c;发现自己的pytorch是装的cpu版的(好像当时是直接加清华源&#xff0c;默认是cpu版本&#xff09;。从官网在线下载速度太慢&#xff0c;还时不时断开连接&#xff0c;我们可以配置conda的清华源去这个问题&#xff0c;但是考虑到是在用…

.NET周刊【11月第4期 2024-11-24】

国内文章 C# 入门深度学习&#xff1a;万字长文讲解微积分和梯度下降 https://www.cnblogs.com/whuanle/p/18551532 这篇文章主要介绍了使用 C# 进行深度学习的方法&#xff0c;特别是微积分在此领域的应用。作者简要讲解了极限、导数等基本概念&#xff0c;并展示了如何在 …

How to monitor Spring Boot apps with the AppDynamics Java Agent

本文介绍如何使用 AppDynamics Java 代理监视 Azure Spring Apps 中的 Spring Boot 应用程序。 使用 AppDynamics Java 代理可以&#xff1a; 监视应用程序使用环境变量配置 AppDynamics Java 代理 在 AppDynamics 仪表板中检查所有监视数据 How to monitor Spring Boot app…

使用ECharts创建带百分比标注的环形图

在数据可视化领域&#xff0c;环形图是一种非常有效的图表类型&#xff0c;它能够清晰地展示各部分与整体的关系。今天&#xff0c;我们将通过ECharts来创建一个带百分比标注的环形图&#xff0c;并详细解释如何实现这一效果。 1. 数据准备 首先&#xff0c;我们定义了一些基础…

基于LSTM的文本多分类任务

概述&#xff1a; LSTM&#xff08;Long Short-Term Memory&#xff0c;长短时记忆&#xff09;模型是一种特殊的循环神经网络&#xff08;RNN&#xff09;架构&#xff0c;由Hochreiter和Schmidhuber于1997年提出。LSTM被设计来解决标准RNN在处理序列数据时遇到的长期依赖问题…

frp软件实现网络穿透

1. 名词 1.1. 网络穿透 网络穿透是一种技术&#xff0c;用于解决内网设备或服务无法直接被外部网络访问的问题。通常&#xff0c;内网设备位于路由器后面&#xff0c;并没有公网 IP 地址&#xff0c;因此外部用户不能直接连接到这些设备。网络穿透通过一些特定的技术手段&…

leetcode3250. 单调数组对的数目 I,仅需1s

题目&#xff1a; https://leetcode.cn/problems/find-the-count-of-monotonic-pairs-i/description/ 不为别的&#xff0c;只是记录下这个超过100%&#xff0c;而且比原先最快的快了一个量级 不知道咋分析&#xff0c;反正得出结论就是&#xff0c;变大不变&#xff0c;变小…

使用docker-compose部署搜索引擎ElasticSearch6.8.10

背景 Elasticsearch 是一个开源的分布式搜索和分析引擎&#xff0c;基于 Apache Lucene 构建。它被广泛用于实时数据搜索、日志分析、全文检索等应用场景。 Elasticsearch 支持高效的全文搜索&#xff0c;并提供了强大的聚合功能&#xff0c;可以处理大规模的数据集并进行快速…

Zabbix 模板翻译自动化教程

在企业 IT 运维管理中&#xff0c;Zabbix 作为一款强大的开源监控平台被广泛应用。而 Zabbix 模板作为监控配置的重要组成部分&#xff0c;用来定义监控项、触发器、图形等。随着国际化的需求增加&#xff0c;Zabbix 模板的翻译工作变得日益重要&#xff0c;特别是在需要为不同…

Springboot小知识(1):启动类与配置

一、启动类&#xff08;引导类&#xff09; 在通常情况下&#xff0c;你创建的Spring应用项目都会为你自动生成一个启动类&#xff0c;它是这个应用的起点。 在Spring Boot中&#xff0c;引导类&#xff08;也称为启动类&#xff0c;通常是main方法所在的类&#xff09;是整个…

数据集-目标检测系列- 海边漫步锻炼人检测数据集 person >> DataBall

数据集-目标检测系列- 海边漫步锻炼人检测数据集 person >> DataBall DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球…

NLP信息抽取大总结:三大任务(带Prompt模板)

信息抽取大总结 1.NLP的信息抽取的本质&#xff1f;2.信息抽取三大任务&#xff1f;3.开放域VS限定域4.信息抽取三大范式&#xff1f;范式一&#xff1a;基于自定义规则抽取&#xff08;2018年前&#xff09;范式二&#xff1a;基于Bert下游任务建模抽取&#xff08;2018年后&a…

手机中的核心SOC是什么?

大家好&#xff0c;我是山羊君Goat。 常常听说CPU&#xff0c;中央处理器等等的&#xff0c;它是一个电脑或单片机系统的核心&#xff0c;但是对于SOC可能相比于CPU了解的人没有那么广泛。 所以SOC是什么&#xff1f; SOC全称是System on Chip&#xff0c;就是片上系统&#…

网络--socket编程--基础

1、网络字节序 已知:内存中的很多数据都有大小端之分,在网络这,网络数据流也是有大小端之分的。 TCP/IP协议规定:网络数据流采用大端字节序(即低地址处放高位字节)。 因此,小端机器发送网络数据流之前,必须转为大端(一般的机器会自动转换): 在网络-本地字节序转换…

Transformers在计算机视觉领域中的应用【第1篇:ViT——Transformer杀入CV界之开山之作】

目录 1 模型结构2 模型的前向过程3 思考4 结论 论文&#xff1a; AN IMAGE IS WORTH 16X16 WORDS: TRANSFORMERS FOR IMAGE RECOGNITION AT SCALE 代码&#xff1a;https://github.com/google-research/vision_transformer Huggingface&#xff1a;https://github.com/huggingf…

<数据集>路面坑洼识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;665张 标注数量(xml文件个数)&#xff1a;665 标注数量(txt文件个数)&#xff1a;665 标注类别数&#xff1a;1 标注类别名称&#xff1a;[pothole] 序号类别名称图片数框数1pothole6651740 使用标注工具&#x…

PySide6 QSS(Qt Style Sheets) Reference: PySide6 QSS参考指南

Qt官网参考资料&#xff1a; QSS介绍&#xff1a; Styling the Widgets Application - Qt for Pythonhttps://doc.qt.io/qtforpython-6/tutorials/basictutorial/widgetstyling.html#tutorial-widgetstyling QSS 参考手册&#xff1a; Qt Style Sheets Reference | Qt Widge…

07.ES11 08.ES12

7.1、Promise.allSettled 调用 allsettled 方法&#xff0c;返回的结果始终是成功的&#xff0c;返回的是promise结果值 <script>//声明两个promise对象const p1 new Promise((resolve, reject) > {setTimeout(() > {resolve("商品数据 - 1");}, 1000)…

qt QGraphicsRotation详解

1、概述 QGraphicsRotation 是 Qt 框架中 QGraphicsTransform 的一个子类&#xff0c;它专门用于处理图形项的旋转变换。通过 QGraphicsRotation&#xff0c;你可以对 QGraphicsItem&#xff08;如形状、图片等&#xff09;进行旋转操作&#xff0c;从而创建动态和吸引人的视觉…