在开发 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 这个依赖是写了版本号的,其余两个没写。
我们知道导入一个依赖需要提供依赖的坐标 (groupId、artifactId、version)
既然 spring-boot-starter-web 和 mysql-connector-java 没有提供 version,那么应该无法正确下载依赖才对。
但实际情况如图
spring-boot-starter-web 和 mysql-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
上面说了 dependency,plugin 也是同理。不信你回头看案例的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.RELEASE 和 spring-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中申明的依赖