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...
相关推荐
shane-u2 小时前
Maven私服搭建与登录全攻略
java·maven
半部论语2 小时前
jdk多版本切换,通过 maven 指定编译jdk版本不生效,解决思路
java·开发语言·maven·intellij-idea
我喜欢山,也喜欢海5 小时前
Jenkins Maven 带权限 搭建方案2025
java·jenkins·maven
kaikaile19955 小时前
Jenkins集成Maven
servlet·jenkins·maven
.生产的驴5 小时前
Docker 部署Nexus仓库 搭建Maven私服仓库 公司内部仓库
java·运维·数据库·spring·docker·容器·maven
.生产的驴10 小时前
Maven 公司内部私服中央仓库搭建 局域网仓库 资源共享 依赖包构建共享
java·maven
Brilliant Nemo11 小时前
五、框架实战:SSM整合原理和实战
maven·mybatis
亮11112 小时前
GITLAB跑gradle项目 不借助maven-publish直接上传到nexus私人仓库
java·gitlab·gradle·maven
极小狐1 天前
极狐GitLab 通用软件包存储库功能介绍
java·数据库·c#·gitlab·maven
Meta391 天前
解决IDEA Maven编译时@spring.profiles.active@没有替换成具体环境变量的问题
spring·maven·intellij-idea