在使用 Maven 创建多模块项目的时候,在父项目的 pom 文件中经常会碰见 <dependencyManagement>
标签的使用,比如如下代码:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
那么它的作用究竟是什么呢?我们直接把标签名翻译过来,其意思为“依赖管理”,是的,它在 Maven 中提供了一种管理依赖版本号的方式。
在常规使用中,一个 Maven 项目如果要引用某个依赖,那么直接就在 dependencies 中添加 dependency 描述所需的依赖坐标信息即可完成。这样就达到了一个要什么,就直接写什么的效果,决定权都在是否用 dependency 指定了引用构件的坐标。
但在实际项目管理过程中,会有多个模块,如果把这些模块所需的依赖各自引入,不仅会导致管理的不方便,更甚会有版本冲突等问题,所以此时应该设计一个全局的依赖管理。也就是说,把整个项目要引用的依赖,事先分析整理好,形成一个全局的集合。当某个 Maven 模块需要具体引用某依赖的时候,直接在集合中指定若干个。这样就可以实现整个项目依赖的全局管理,不至于零碎地分布在每个 Maven 模块中。
正是基于这样的考虑,就产生 dependencyManagement 的设计,在此标签元素中声明所需依赖的版本号等信息,那么所有子项目再次引入此依赖 jar 包时则无需显式的列出版本号。Maven 会沿着父子层级向上寻找拥有 dependencyManagement 元素的项目,然后使用它指定的版本号。
例如,在父项目中的 pom.xml 如下:
<properties>
<springframework.version>1.2.3.RELEASE</springframework.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${springframework.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
此配置即声明了 Spring Boot 的版本信息,注意其中还有 type(打包类型) 和 scope 标签,import 表示当前项目的依赖可用于另外一个项目,并且 import 范围只有在 denpendencyManagement 元素下才有效果,由于其范围有特殊性,一般都是指向打包类型为 pom 的模块。
如果某子项目中需要使用上述的依赖,直接引入即可,并且不必再指定版本号,它会自动继承父类的版本信息。子项目的 pom.xml 如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
注意:
- 父项目中的 dependencyManagement 中定义的只是依赖的声明,并不实现引入,因此子项目需要显式的声明需要用的依赖。
- 如果子项目重新指定了依赖的版本号,那么它会引入一个新的依赖而不是从父项目继承。
- scope=import 只能用在 dependencyManagement 里面,且仅用于 type=pom 的 dependency。
dependencies 和 dependencyManagement 的不同:
- 父项目中使用 dependencies 引入依赖,子项目会自动继承父项目中的全部依赖项(全部继承);
- 父项目中使用 dependencyManagement 声明依赖,并不会引入依赖,子项目需要时再引入。
dependencyManagement 的优势:
- 如果有多个子项目都引用同一样依赖,则可以避免在每个使用的子项目里都声明一个版本号;
- 当想升级或切换到另一个版本时,只需要在顶层父容器里更新,而不需要逐个修改子项目;
- 另外如果某个子项目需要另外的一个版本,只需要声明 version 即可。