乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 介绍与核心概念

    • Maven是什么
    • 约定优于配置
  • 第2章 安装与配置

    • 安装与验证
    • settings.xml
    • 本地仓库与镜像
  • 第3章 POM与项目坐标

    • POM
    • GAV坐标
    • packaging
  • 第4章 标准目录布局

    • 标准目录布局
  • 第5章 依赖机制

    • dependencies
    • scope
    • 依赖传递
    • 依赖冲突与调解
    • exclusions
    • optional
    • dependencyManagement
  • 第6章 仓库

    • 仓库体系
    • 本地仓库
    • 远程仓库与镜像
    • 私服
  • 第7章 构建生命周期

    • 生命周期概述
    • clean 生命周期
    • default 生命周期
    • site 生命周期
    • 生命周期与插件绑定
  • 第8章 插件

    • 插件概述
    • maven-compiler-plugin
    • maven-surefire-plugin
    • maven-war-plugin
  • 第9章 继承与聚合

    • parent继承
    • 聚合
    • BOM
    • properties
  • 第10章 属性与资源过滤

    • 资源过滤
    • Profile
  • 第11章 常用命令

    • mvn compile
    • mvn test
    • mvn package
    • mvn clean
    • mvn install
    • mvn dependency:tree
  • 第12章 常见问题与最佳实践

    • 依赖冲突排查
    • 最佳实践

dependencyManagement

本章是"依赖机制"篇章的收官之作。理解 dependencyManagement,是掌握 Maven 多模块项目版本治理、统一技术栈和消除子模块版本碎片的关键。


核心机制

dependencyManagement 用于在父 POM 中集中声明依赖的版本、scope 和 exclusions,子模块在 dependencies 中引用时可以省略 version。它只管理版本信息,不实际引入依赖。

这句话包含两个核心要点:

  1. 集中管理:版本号写在父 POM 一处,所有子模块共享
  2. 不引入依赖:子模块必须在 dependencies 中再次声明,依赖才真正进入 classpath

这是最容易混淆的一对概念:

维度dependencyManagementdependencies
所在位置父 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 Boot 2.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 如何编排构建过程。

上一页
optional