快速解决 Maven 版本冲突指南 !

之前我们讲解了Maven的配置,那么这次我们就讲讲一些更贴近日常维护的东西,就是maven的版本控制,在一个工程的正常生命周期内。

我们可能会因为功能变更、性能提升、修复漏洞等多种原因,去更换第三方库或框架。此时就容易发生版本冲突,本期我们就介绍一下相关知识,以及如何解决版本冲突。

一、版本冲突的原因

内部冲突: 当一个项目直接依赖了不同的版本号,可能会导致冲突。

图片

模块间冲突: 一个库内部不同模块之间使用了不同的版本号,或互相引用时,也可能导致冲突。

图片

图片

二、查看与分析冲突

1. 依赖树

① 原生命令

使用dependency:tree命令查看依赖树:maven提供了一个命令mvn dependency:tree,可以查看项目的依赖树,从而帮助我们分析版本冲突的原因。例如:

复制代码
mvn dependency:tree

运行上述命令,maven会打印出项目的依赖树,我们可以根据这个依赖树找到冲突的版本并解决它。

图片

② Maven Helper

原生命令的可视化成都有限,所以对于开发者来说,最常用的还是在IDEA上安装maven helper插件了,我们可以在IDEA插件市场里将其安装上

图片

安装并启用成功后,我们打开某pom文件,就能看到该pom下的依赖情况了

图片

2. 冲突分析

① 查看冲突

在安装完Maven Helper后, 我们其实可以直接看到有哪些冲突,如下图,项目上就存在如下的jar包冲突,当我们选中poi-oomxl后,右边则具体显示了造成该冲突的具体情况

图片

不难看出,项目中用了多个不同的组件,而这些组件又使用了不同版本的 poi-oomxl,最终导致在项目中引用了三个不同版本的 poi-oomxl

② maven的版本规则

不难看出,尽管项目中依赖了三个不同的版本,但最后我们实际在项目中存在的却只会有一个 poi-oomxl 组件。那么在发生冲突时,maven 到底会取用哪个版本的组件呢?这就涉及到maven的版本规则

就近原则(最短路径)

多条路径时,选择最短的路径(依赖的层级小),如下,就会选用第二条路径,最后选择的版本为 version 0.0.2

    1. A ---> C ---> D ---> E ---> X(version 0.0.1)
    1. A ---> F ---> X(version 0.0.2)

声明顺序

在路径相同的情况下,Maven会选择最先声明的版本。如下,就会选用第一条路径,最后选择的版本为 version 0.0.1

    1. A ---> C ---> X(version 0.0.1)
    1. A ---> F ---> X(version 0.0.2)

我们来看一个例子,这里maven为我们选择了4.1.2,就是因为我们直接在pom文件里指定了版本4.1.2,所以它就只有一层,是最短路径。

图片

如果我们把pom里的直接引用内容注释掉

图片

那么就会用新的最短路径了,最终选取的版本为4.1.1

图片

③ 版本选择

一般来说,我们相信组件都具有"向下兼容"的能力,即低版本组件的功能,在高版本上应该也能使用。所以当出现组件冲突时,我们往往选择保留目前的最高版本。

三、maven解决版本冲突的方法

1. 排除依赖

当我们发现某个依赖引起了冲突,可以使用 maven 的exclude标签排除它。例如:

xml 复制代码
<dependencies>
    <dependency>
        <groupId>example.group</groupId>
        <artifactId>example.artifact</artifactId>
        <version>1.0</version>
        <!-- <exclusions> 元素用于排除指定的依赖 -->
        <exclusions>
            <exclusion>
                <groupId>conflict.group</groupId>
                <artifactId>conflict.artifact</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

上述代码中,假如 example.artifact 引用了某个版本的 conflict.artifact ,与其他地方引用的conflict.artifact 发生冲突,我们就可以这样,把example.artifact 里的 conflict.artifact 剔除掉。如果是使用插件的话,则更方便,右键选中组件,即可以快速进行 exclude

图片

2. 依赖管理

但是使用 exclusions 也有比较麻烦的地方,exclusions 只对当前依赖有效,并不会影响其他依赖。因此,如果项目中有多个依赖引入了相同的冲突依赖,需要在每个依赖中都使用 exclusions 元素进行排除。所以,有时候我们希望明确指定某个依赖的版本号,可以使用maven的dependencyManagement标签来达到目的

① 单模块

如下 在pom文件中,我们加入dependencyManagement,并在其中指定了poi-ooxml的版本号为4.1.2(注意dependencyManagement只有管理信息的功能,并没有真实引用poi-ooxml,所以后面的引用段落仍然要保留)

图片

然后在引用的段落里,把版本号清除掉

图片

此时我们再去看引用情况,就会发现所有的引用全部变成了 4.1.2,不再有冲突提示

图片

② 跨模块处理

多模块的管理,更加需要使用 dependencyManagement 来确保各子模块在使用相同版本的组件。所以此时需要在父POM文件中加入dependencyManagement

xml 复制代码
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>parent-project</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
       <module>../child1</module>
       <module>../child2</module>
       <module>../child3</module>
    </modules>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>4.1.2</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

然后在所有子模块的pom文件里只保留引用,不再指定版本号,如下

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
    </dependency>
</dependencies>

此时,所有子模块凡是引用了poi-ooxml的, 就都会指定使用 4.1.2 版本,而不会再产生冲突了。当然,如果某个子项目需要指定一个特殊的版本号时,只需要在自己项目的pom.xml中显示声明一个版本号即可,因为就近原则的关系,该模块会使用自己指定的版本号

四、结论

在软件开发过程中,版本冲突是一个常见的问题。我们本次就了解maven在发生版本冲突时,该如何查看冲突情况,并知道maven选择哪个版本是遵循就近原则、与声明顺序的。而在处理时我们可以使用排包(exclusive)法,或者显示的使用 dependencyManagement 来指定版本号。

相关推荐
灰小猿2 小时前
Spring前后端分离项目时间格式转换问题全局配置解决
java·前端·后端·spring·spring cloud
算法与编程之美2 小时前
理解Java finalize函数
java·开发语言·jvm·算法
怕什么真理无穷2 小时前
C++面试4-线程同步
java·c++·面试
lkbhua莱克瓦242 小时前
Java基础——常用算法5
java·开发语言·笔记·github
牢七2 小时前
javan小案例。
java
星释3 小时前
Rust 练习册 :Luhn与校验算法
java·算法·rust
程序猿_极客3 小时前
【2025】16届蓝桥杯 Java 组全题详解(省赛真题 + 思路 + 代码)
java·开发语言·职场和发展·蓝桥杯
毕设源码-邱学长3 小时前
【开题答辩全过程】以 “万家电器”仓库管理系统的设计与实现为例,包含答辩的问题和答案
java·eclipse
摇滚侠3 小时前
Spring Boot3零基础教程,响应式编程的模型,笔记109
java·spring boot·笔记