dependencyManagement
本章是"依赖机制"篇章的收官之作。理解
dependencyManagement,是掌握 Maven 多模块项目版本治理、统一技术栈和消除子模块版本碎片的关键。
核心机制
dependencyManagement 用于在父 POM 中集中声明依赖的版本、scope 和 exclusions,子模块在 dependencies 中引用时可以省略 version。它只管理版本信息,不实际引入依赖。
这句话包含两个核心要点:
- 集中管理:版本号写在父 POM 一处,所有子模块共享
- 不引入依赖:子模块必须在
dependencies中再次声明,依赖才真正进入 classpath
这是最容易混淆的一对概念:
| 维度 | dependencyManagement | dependencies |
|---|---|---|
| 所在位置 | 父 POM 或当前 POM 的 <dependencyManagement> | 当前 POM 的 <dependencies> |
| 是否引入依赖 | ❌ 不引入,只声明版本 | ✅ 引入,进入 classpath |
| 子模块能否省略 version | ✅ 可以 | ❌ 不可以(除非父 POM 已管理) |
| 作用范围 | 版本锁定、scope 统一、exclusions 预设 | 实际构建、编译、测试、打包 |
| 典型用途 | 统一全公司技术栈版本 | 声明本项目具体需要什么 |
关键记忆法:
dependencyManagement= 版本字典:只定义"如果用这个依赖,版本是多少"dependencies= 购物清单:实际决定"我要买哪些东西"
父 POM 中的 dependencyManagement
<!-- 父 POM:feixiang-parent/pom.xml -->
<project>
<groupId>com.feixiang</groupId>
<artifactId>feixiang-parent</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<!-- 统一声明版本,不引入依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
子 POM 中的 dependencies
<!-- 子模块:employee-system/pom.xml -->
<project>
<parent>
<groupId>com.feixiang</groupId>
<artifactId>feixiang-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>employee-system</artifactId>
<dependencies>
<!-- 省略 version,从父 POM 继承 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 不需要写 <version>2.7.0</version> -->
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- 不需要写 version 和 scope,从父 POM 继承 runtime -->
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<!-- 不需要写 version 和 scope,从父 POM 继承 test -->
</dependency>
</dependencies>
</project>
子模块省略 version 后的实际效果
Maven 在构建子模块时,会自动将父 POM dependencyManagement 中的版本、scope、exclusions 合并到子模块的依赖声明中。子模块的 junit-jupiter 实际上等效于:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version> <!-- 从父 POM 继承 -->
<scope>test</scope> <!-- 从父 POM 继承 -->
</dependency>
生活类比:公司的"采购标准清单"
想象飞翔科技有一个采购部(父 POM 的 dependencyManagement):
- 采购部制定了一份《标准供应商名录》,上面写着"如果需要买打印机,选惠普 LaserJet;如果需要买笔记本,选 ThinkPad X1"。这份名录不强制每个部门都买打印机和笔记本,只是规定"如果买,必须买这个型号"。
- 技术部(子模块
employee-system)说"我需要笔记本",采购员看了一眼名录,直接下单 ThinkPad X1——不需要技术部自己查型号、比价、填版本号。 - 财务部(子模块
payroll-service)说"我需要打印机和笔记本",同样按名录采购。 - 如果明年采购部更新名录(升级父 POM 版本),把笔记本换成 ThinkPad X2,所有部门自动跟着升级,不需要逐个通知。
dependencyManagement 就是这份《标准供应商名录》:不强制购买,但统一规格。
图示
上图展示了 dependencyManagement 的工作方式:父 POM 像一本"版本字典",子模块像"查字典的人"。子模块在 dependencies 中声明需要什么,Maven 自动去父 POM 的 dependencyManagement 里查对应的版本和 scope。不同子模块可以引用字典中的不同条目,也可以都引用同一套条目,实现版本统一。
完整示例
场景
飞翔科技有 5 个微服务模块:employee-system、payroll-service、attendance-service、gateway、admin-portal。CTO 大翔发现各模块的 Spring Boot 版本混乱——有的是 2.6.0,有的是 2.7.0,还有的是 2.7.3,导致线上环境行为不一致。架构师白歌决定用 dependencyManagement 统一全公司技术栈,后端小崔负责改造 employee-system 和 payroll-service 两个模块。
操作前:版本碎片化的灾难
employee-system/pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.6.0</version> <!-- ❌ 旧版本 -->
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version> <!-- ❌ 小版本不一致 -->
</dependency>
</dependencies>
payroll-service/pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.3</version> <!-- ❌ 又不一样 -->
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
</dependencies>
问题:
employee-system的 Spring Boot2.6.0缺少2.7.0引入的安全补丁- 两个服务的 MySQL 驱动小版本不同,连接池行为有细微差异
- 新成员小崔从
employee-system调到payroll-service时,发现同样的注解在不同版本里行为不同 - 李眉部署时,需要为每个服务单独检查版本兼容性矩阵
操作后:父 POM 统一治理
白歌创建父 POM:
<!-- feixiang-parent/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.feixiang</groupId>
<artifactId>feixiang-parent</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<properties>
<spring-boot.version>2.7.0</spring-boot.version>
<mysql.version>8.0.30</mysql.version>
<junit.version>5.8.2</junit.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- 统一 Spring Boot 版本 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- 统一数据库驱动版本和 scope -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
<scope>runtime</scope>
</dependency>
<!-- 统一测试框架 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
小崔改造 employee-system/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>
<parent>
<groupId>com.feixiang</groupId>
<artifactId>feixiang-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>employee-system</artifactId>
<dependencies>
<!-- 省略 version,继承父 POM 的 2.7.0 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 省略 version 和 scope,继承 runtime -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 省略 version 和 scope,继承 test -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
</dependency>
</dependencies>
</project>
payroll-service/pom.xml 同样改造,结构几乎相同,只是 artifactId 不同。
执行构建验证:
cd employee-system
mvn dependency:tree
输出:
com.feixiang:employee-system:jar:1.0.0
+- org.springframework.boot:spring-boot-starter-web:jar:2.7.0:compile
| +- ...
+- org.springframework.boot:spring-boot-starter-data-jpa:jar:2.7.0:compile
| +- ...
+- mysql:mysql-connector-java:jar:8.0.30:runtime
\- org.junit.jupiter:junit-jupiter:jar:5.8.2:test
变化分析:
employee-system和payroll-service的 Spring Boot 版本统一为2.7.0,行为一致- MySQL 驱动统一为
8.0.30,scope 统一为runtime - 子模块的
pom.xml精简了 50% 以上,小崔不需要记住每个依赖的版本号 - 李眉的部署脚本只需检查父 POM 版本,就能知道所有子模块的技术栈
- 升级时,白歌只需修改父 POM 里的
<spring-boot.version>,所有子模块自动跟随
易错点与常见问题
误区一:dependencyManagement 会自动引入依赖
错误认知:"我在父 POM 的 dependencyManagement 里写了 spring-boot-starter-web,子模块就能直接用了。"
纠正:dependencyManagement 只声明版本,不引入依赖。子模块必须在 dependencies 中再次声明(可以省略 version),依赖才真正进入 classpath。如果子模块不写 dependencies,spring-boot-starter-web 不会出现在子模块的任何 classpath 中。
误区二:子模块写了 version 会覆盖父 POM
错误认知:"子模块在 dependencies 里写了 <version>2.6.0</version>,会覆盖父 POM 的 2.7.0。"
纠正:是的,会覆盖。子模块显式声明的 version 优先级高于父 POM 的 dependencyManagement。这既是灵活性(允许个别模块临时降级),也是风险点(破坏统一治理)。白歌应该在团队规范中明确:子模块禁止在 dependencies 中写 version,所有版本由父 POM 锁定。
误区三:dependencyManagement 只能用在父 POM
错误认知:"dependencyManagement 是父 POM 专属,普通项目不能用。"
纠正:dependencyManagement 可以出现在任何 POM 中,包括单模块项目。在单模块项目中,它的作用是提前声明版本,供本项目后续的 dependencies 引用。但最常见的用法仍然是多模块项目的父 POM,因为那是版本统一治理的最大收益场景。
小结
dependencyManagement 是 Maven 多模块项目的"版本字典",它在父 POM 中集中声明依赖的版本、scope 和 exclusions,子模块在 dependencies 中引用时可以省略 version。它只管理元数据,不实际引入依赖——子模块必须再次声明才能将依赖纳入 classpath。与 dependencies 配合,dependencyManagement 实现了全公司技术栈的版本统一、减少重复配置、简化升级流程。
本章与全局的关系:本章是"依赖机制"篇章的终点。至此,你已经掌握了声明依赖(dependencies)、理解传递(传递依赖)、控制边界(scope)、解决冲突(调解机制)、精确修剪(exclusions)、可选传递(optional)和统一治理(dependencyManagement)的完整工具链。下一篇章将进入"生命周期与插件",讲解 Maven 如何编排构建过程。