maven依赖冲突加载顺序与解决

依赖冲突加载顺序

介绍了项目中同时引用了相同依赖的不同版本,也就是冲突,maven 是如何选择的。了解了有助于解决项目中的依赖问题

先说结论:

  • 直接依赖的会引用后申明的依赖
  • 间接依赖使用路径长度最短的,如果长度一样,则优先申明的最终加载
  • 有父 pom 的子 pom 中的会覆盖父 pom 的依赖版本
  • <dependencyManagement> 管理的版本,子模块 pom 中间接依赖的版本也被锁死

优先级(运行子 pom):
<dependencyManagement> > 子 pom(直接依赖) > 父 pom > 间接依赖

直接依赖

xml 复制代码
        <!-- 直接依赖 -->
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>2.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.19.4</version>
        </dependency>

项目的 pom 文件直接引用依赖

结论:maven 会引用后申明的依赖

  1. 这种情况几乎不会有,谁会在一处同时申明2个相同依赖不同版本的依赖,假使出现了,了解即可。

间接依赖

假设 module-starter 的 pom 如下:

xml 复制代码
        <dependency>
            <groupId>com.meizi</groupId>
            <artifactId>module4</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>com.meizi</groupId>
            <artifactId>module1</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

module4 引用 protobuf-java:3.11.4

module1 引用 protobuf-java:2.2.0

最终使用的是 protobuf-java:3.11.4

结论:间接依赖使用优先申明的

假设 module-starter 的 pom 如下:

xml 复制代码
        <dependency>
            <groupId>com.meizi</groupId>
            <artifactId>module2</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>com.meizi</groupId>
            <artifactId>module4</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

module4 引用 protobuf-java:3.11.4 (路径长度1)

module2 引用 module3 引用 protobuf-java:3.19.4(路径长度2)

最终使用的是 protobuf-java:3.11.4

结论:间接依赖使用路径长度最短的,如果长度一样,则优先申明的最终加载

父 pom 中的依赖

父 pom 如下:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<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.meizi</groupId>
    <artifactId>learnmaven</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
        <module>module1</module>
        <module>module2</module>
        <module>module3</module>
        <module>module4</module>
        <module>module-starter</module>
    </modules>

    <dependencies>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.11.4</version>
        </dependency>
    </dependencies>

</project>

module1 pom 如下:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<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">
    <parent>
        <artifactId>learnmaven</artifactId>
        <groupId>com.meizi</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>module1</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>2.2.0</version>
        </dependency>

    </dependencies>

</project>

结论:子 pom 中的会覆盖父 pom 的依赖版本

  1. 如果间接依赖和父 pom 中的依赖出现,以父 pom 优先

版本锁定 <dependencyManagement>

  • 使用

    如下在父 pom 申明:

    xml 复制代码
        <!-- learnmaven 父pom-->
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>com.google.protobuf</groupId>
                    <artifactId>protobuf-java</artifactId>
                    <version>2.2.0</version>
                </dependency>
            </dependencies>
        </dependencyManagement>
  • 子模块 pom 可不用写版本

    子模块中与 dependencyManagement 匹配的最小信息集是这四个 {groupId, artifactId, type, classifier},因为在大多数情况下都是 type=jar classifier=null,所以如果不写,maven默认,如果不是jar的需要指定。

    xml 复制代码
            <!-- module-starter 子pom-->
            <dependency>
                <groupId>com.google.protobuf</groupId>
                <artifactId>protobuf-java</artifactId>
            </dependency>
  • 子模块 pom 中间接依赖的版本也被锁死

    xml 复制代码
            <!-- module-starter 子pom-->
            <dependency>
                <groupId>com.meizi</groupId>
                <artifactId>module4</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
    
            <dependency>
                <groupId>com.meizi</groupId>
                <artifactId>module3</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>

    从可传播依赖并入的依赖的版本也受此限制。

依赖冲突解决

了解了依赖冲突加载顺序,一定程度上可以解决冲突。 由于没有依赖传播级数的限制,会有可能出现循环依赖。 所以有一些方式来限制依赖传播

依赖调解(最短路径)

最短路径 项目的依赖树最近的依赖

如下依赖:

text 复制代码
  A
  ├── B
  │   └── C
  │       └── D 2.0
  └── E
      └── D 1.0

会使用 D 1.0,因为他的路径最短,如果你想使用 D 2.0 可以在 A 项目中直接添加 D 2.0 依赖,如下

text 复制代码
  A
  ├── B
  │   └── C
  │       └── D 2.0
  ├── E
  │   └── D 1.0
  │
  └── D 2.0 

版本锁定

<dependencyManagement>,使用见上文

依赖作用域

<scope> 作用域,限制依赖传递和决定何时依赖包含在类路径中

提供如下选项:

  • compile 默认,编译依赖项在项目的所有类路径中都可用。此外,这些依赖关系被传播到依赖的项目。
  • runtime 在编译中不需要,在执行中需要
  • test 表明该依赖在正常使用中不是必须的,并且仅在测试编译和执行阶段可用,这个作用域不是可传递的
  • system 除了您必须提供显式包含它的JAR之外,此作用域与提供的作用域类似。工件总是可用的,不会在存储库中查找。
  • provided 类似compile,表明这个依赖是提供的,在运行时会有外部提供(例如tomcat),仅在编译和测试,是不可传递的
  • import 只用在pom的 <dependencyManagement> 部分,它指示该依赖项将被指定POM的<dependencyManagement>部分中的有效依赖项列表所替换。

排除依赖

通过使用 exclusion 可以将不需要的依赖排除掉

xml 复制代码
        <dependency>
            <groupId>com.meizi</groupId>
            <artifactId>module4</artifactId>
            <version>1.0-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>com.google.protobuf</groupId>
                    <artifactId>protobuf-java</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

此时,引入module4,将不会引入 protobuf-java

可选依赖

<optional>,默认false不可选,可传播; true 可选,可认为是被排除了,不可传播

xml 复制代码
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.11.4</version>
            <!-- 默认false不可选,可传播; true 可选,可认为是被排除了,不可传播 -->
            <optional>true</optional>
        </dependency>

如果想排除此依赖,也可将 optional 设置为 true

依赖管理

导入依赖

在大型项目中,很难通过继承管理所有依赖,因为 maven 是单继承的。

为了解决这个,项目可以从其他项目导入。通过申明一个 type 是 pom,scope 是 import 的工件

xml 复制代码
        <!-- learnmaven 父pom-->
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>io.netty</groupId>
                    <artifactId>netty-bom</artifactId>
                    <version>4.1.29.Final</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>

所有 netty-bom 中 dependencyManagement 所管理的都会被合并过来。

xml 复制代码
        <!-- learnmaven 父pom-->
        <dependencyManagement>
            <dependencies>

                <dependency>
                    <groupId>io.netty</groupId>
                    <artifactId>netty-bom</artifactId>
                    <version>4.1.79.Final</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>

                <dependency>
                    <groupId>io.netty</groupId>
                    <artifactId>netty-bom</artifactId>
                    <version>4.1.29.Final</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>

如上,4.1.79.Final 将被使用,因为他先申明

并且导入是递归的,例如,netty-bom 导入了另一个 pom,那么这个项目也会导入

bom poms

Bill of Materials (BOM) 材料清单

参考文献

  1. maven.apache.org/guides/intr...
相关推荐
摆烂且佛系15 小时前
IDEA Maven 仓库配置优先级
github·maven·intellij-idea
momo_via1 天前
maven下载与安装及在IDEA中配置maven
java·maven·intellij-idea
李贺梖梖1 天前
Maven 设置项目编码,防止编译打包出现编码错误
java·maven
洛克大航海1 天前
Ubuntu安装JDK与Maven和IntelliJ IDEA
ubuntu·jdk·maven·intellij idea
假客套1 天前
2025 FastExcel在Java的Maven项目的导出和导入,简单易上手,以下为完整示例
java·maven·fastexcel
有梦想的攻城狮1 天前
Maven中的settings.xml文件配置详解
xml·java·maven·settings.xml
李贺梖梖2 天前
maven本地仓库有相应的依赖,依旧会从远程仓库拉取问题的原因及解决
maven
纳于大麓2 天前
Android Maven私服搭建(Windows)
java·maven
诸神缄默不语2 天前
Maven用户设置文件(settings.xml)配置指南
xml·java·maven
安逸sgr3 天前
SpringMVC启动流程
java·jvm·spring·spring cloud·eclipse·tomcat·maven