maven 依赖版本管理— dependencyManagement

15,566 阅读6分钟

在开发 spring boot 项目时,你是否遇到过,有些依赖即使不写版本号也能下载到某一版本的依赖。

比如下面这个案例

案例

pom文件

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.9.RELEASE</version>
        <relativePath/>
    </parent>

    <groupId>com.wqlm</groupId>
    <artifactId>boot</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

可以看到 <dependencies> 下配置了三个依赖项

  • spring-boot-starter-web
  • mysql-connector-java
  • mybatis-spring-boot-starter

这个项目虽然引用了三个依赖,但只有 mybatis-spring-boot-starter 这个依赖是写了版本号的,其余两个没写。

我们知道导入一个依赖需要提供依赖的坐标 (groupIdartifactIdversion)

参考 maven 坐标

既然 spring-boot-starter-webmysql-connector-java 没有提供 version,那么应该无法正确下载依赖才对。

但实际情况如图

spring-boot-starter-webmysql-connector-java 都能找到对应都版本,这是为什么。

parent

不知道大家注意到没,该 pom 是有 parent 的

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.9.RELEASE</version>
    <relativePath/>
</parent>

也就是说该项目继承自 spring-boot-starter-parent 项目。具体继承了那些东西,我们点进去及可以看到了

如下图 spring-boot-starter-parent 又继承自 spring-boot-dependencies

再点进去可以看到 spring-boot-dependencies 的pom文件如下

可以看到 spring-boot-dependencies 没有 parent, 说明它是顶级pom,其中 <properties> 内定义了很多版本号,mysql-connector-java 的版本号就在里面

是不是和上面看到的版本号一样,都是8.0.17

spring-boot-dependencies.pom

下面是 spring-boot-dependencies-2.1.9.RELEASE.pom 中的部分内容

注意以下三个组件

  • properties
  • dependencyManagement
  • pluginManagement

其中 properties 定义了一系列的版本号,并且在 dependencyManagement 中使用了版本号。注意这个 dependencyManagement,它的下级是我们最常用的 dependencies 组件

我们知道 dependencies 组件是用来引入依赖的,那在外面包上一层 dependencyManagement 是什么意思呢? 它的意思是,声明 dependencies 中的依赖,但不引用!!! 那什么时候引用呢?当子项目中配置了一个 dependency ,并且这个 dependency没写版本号,且在 dependencyManagement 中声明过 时才引用。

这就是为什么有的依赖包需要写版本号,有的不需要写。那些不需要写版本号的依赖,其实在它的父pom的dependencyManagement中已经申明好了,而且不仅申明了 version,有的还申明了 exclusions ,用于剔除某些冲突的依赖

pluginManagement

上面说了 dependencyplugin 也是同理。不信你回头看案例的pom文件,它里面就定义了一个 spring-boot-maven-plugin 的插件,也没有写版本号。

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
</plugin>

它的版本号同样是在父pom的 pluginManagement 中申明好的

dependencies 组件

dependencies 组件用于导入依赖,且所有 dependencies 里的依赖都会被子项目继承。

公共依赖版本管理

像 spring boot 这种大体量的框架,旗下子项目众多,比如

  • spring-boot-starter-web
  • spring-boot-starter-amqp
  • spring-boot-starter-data-redis
  • ...

这些子项目也有各自的依赖,其中有些依赖是公共的,比如

spring-boot-starter-web-2.1.9.RELEASEspring-boot-starter-data-redis--2.1.9.RELEASE 的依赖结构如下

他们都依赖了 spring-boot-starter,既然都是都是 spring-boot 的子项目,并且版本号也一样,那么他们两个依赖的 spring-boot-starter 的版本也应该要一样,不然同时引用他们两个就会出现依赖冲突!

如何保证所有子项目的公共依赖的版本一致呢,总不能在每个子项目里写死吧,这样手动去管理得累死。

所以 spring boot 将所有公共依赖抽离出来,放到 spring-boot-dependencies 中来管理。 所有的子项目都直接或间接继承自 spring-boot-dependencies,这样同一个版本的所有的子项目的公共依赖就都一样了。

但这样做带来了一个问题,每个子项目都继承了所有都公共依赖,解决这件事情,就需要使用 maven 的 dependencyManagement 组件

dependencyManagement 组件

作用用来申明依赖,但不导入。

dependencies 组件用于导入依赖,注意两者区别

特性

  • 子项目不会继承 dependencyManagement 组件中声明的依赖。
  • 但如果子项目想导入某个父pom 中 dependencyManagement 中的依赖,只需要填写 groupId 和 artifactId ,不需要填写版本号,maven会自动去父pom 的 dependencyManagement 中找对应的 version,包括scope、exclusions等

使用 dependencyManagement 组件后,所有的子项目只需要在父pom 的 “公共依赖声明池” 中挑选自己想要的依赖,而不用关心版本。这样即不会继承到不需要的依赖,又统一了依赖的版本

如果想统一调整所有子项目某个依赖的版本,只需要在父pom 里更新。不需要修改任何一个子项目。

如果某个子项目不想使用公共的版本号,只需要在 dependency 中加上版本号,子项目就会使用自定义的版本号,不会继承父类版本号。

spring boot 就是通过这样来统一管理依赖版本的

基于这样的特性, dependencyManagement 组件 一般用于统一管理子项目的公共依赖的版本

依赖版本的查找

Maven会沿着父子层次向上走,直到找到一个拥有 dependencyManagement 组件的项目,然后在其中查找,如果找到则返回申明的依赖,没有继续往下找。

dependencies 与 dependencyManagement 的区别

dependencies

  • 引入依赖
  • 即使子项目中不写 dependencies ,子项目仍然会从父项目中继承 dependencies 中的所有依赖项

dependencyManagement

  • 声明依赖,并不引入依赖。
  • 子项目默认不会继承父项目 dependencyManagement 中的依赖
  • 只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承(version、exclusions、scope等读取自父pom)
  • 子项目如果指定了依赖的具体版本号,会优先使用子项目中指定版本,不会继承父pom中申明的依赖