快速解决 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 来指定版本号。

相关推荐
小怪吴吴16 小时前
idea 开发Android
android·java·intellij-idea
嘻嘻哈哈樱桃16 小时前
牛客经典101题题解集--动态规划
java·数据结构·python·算法·职场和发展·动态规划
一次旅行16 小时前
IDEA安装CC GUI新手指南
java·ide·intellij-idea
超梦dasgg16 小时前
Spring AI 智能航空助手项目实战
java·人工智能·后端·spring·ai编程
counting money17 小时前
Spring框架基础(配置篇)
java·后端·spring
秋917 小时前
OceanBase与GreatSQL在Java应用中的性能调优方法有哪些?
java·开发语言·oceanbase
今天又在写代码17 小时前
并发问题解决
java·开发语言·数据库
老王以为18 小时前
前端视角下的 Java
java·javascript·程序员
看腻了那片水18 小时前
开源一个对业务代码零侵入的透明数据治理框架 —— 【sangsang】
java·mybatis