本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog2,之前的微信号好友位已满,备注:返现
受密码保护的文章请关注“业余草”公众号,回复关键字“0”获得密码
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
腾讯云】1核2G5M轻量应用服务器50元首年,高性价比,助您轻松上云
最近同事问我了一个好问题,大致的意思是,他搞不明白 Spring Boot、Spring Cloud 体系中 pom.xml 的各种配置。
说白了,就是 Parent、dependencyManagement、dependency、pluginManagement、plugins 等这些配置项,各有什么作用,以及是怎么配合的。为什么要搞这些层层套娃的结构?
哎,要解释清楚这个问题呀,得从软件工程上的角度,架构上的角度来说。那接下来,我们就一起搞懂这些眼花缭乱的配置标签,来一次彻底的梳理。
Maven 配置的五虎将
这五虎上将的配置,其实就是各司其职的架构设计而已。先来看看 parent。
parent 的作用
parent 是 Maven 项目继承机制的核心。我们常见于下面这样的定义。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.1</version>
</parent>
这个定义,让我们实际上继承了“上百个”精心配置的默认设置。
- 依赖版本管理:Spring Boot 所有官方组件的版本号
- 插件默认配置:编译插件、打包插件的优化参数
- 资源过滤规则:application.yml 中的变量替换
- Profile 预设:开发、测试环境的默认配置
- 编码与格式化:UTF-8 编码、Java 版本等基础设置
简单来说,它的作用就是,继承规则。统一版本、构建规范、依赖管理、约定插件,减少重复配置,让子项目开箱即用。
dependencyManagement 的作用
接下来,我们来看看 dependencyManagement,这是最容易被误解的元素。它其实是只声明版本,不引入依赖。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2025.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
这段配置是告诉 Maven,更是约束其它使用者。如果你想用 Spring Cloud 组件,我提供了版本清单,但你必须用 dependency 显式声明需要的模块。
需要注意的是,在 dependencyManagement 中的依赖,子项目不会自动获得。这也是为什么一些同事有疑惑的地方,明明 dependencyManagement 有了配置,为什么还需要 dependency 一遍?
这就是 dependencyManagement 的作用,它是一个弱规范,弱约束,约束版本,约束依赖用的。
有了它,子项目只需写 groupId 和 artifactId,无需指定 version。尤其是公司项目很多的时候,这种规范的好处越能得到体现。
dependencies 的作用
dependencies 才是真正的依赖加载器。
这才是实际引入 Jar 包的地方。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 无需版本号,从 parent 继承 -->
</dependency>
</dependencies>
说白了,只有写在这里的依赖,才会出现在你的 classpath 中,参与编译和运行。
当然,这里也可以跳出 3 界之外,自行引入其它依赖,指定其它版本。
但一般不建议这样做,这样会破坏整个集体公司的依赖规范体系。有些管理严的公司,甚至限制只有指定的人员能改子项目或父项目的 pom 依赖,避免出现“野依赖”。子项目的 pom 只有研发人员在调试时进行变更,调试通过后申请有权限的人员变更,并由他们 push。
pluginManagement 的作用
与 dependencyManagement 理念相同,但作用于构建插件。
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>
</plugins>
</pluginManagement>
它的作用是,统一插件版本和配置,但不会自动执行,需要在子项目的plugins中显式引用。
plugins 的作用
如果有 pluginManagement,那么再引入 plugin 的时候,也可以无需自定版本。
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!-- 继承 pluginManagement 的配置 -->
</plugin>
</plugins>
写在plugins中的插件会绑定到 Maven 生命周期(compile/package 等),真正参与构建过程。
完整的 dependency 流向图
pom.xml 里的各个标签,相互协作,默契配合,共同串联起整个 jar 依赖与构建打包部署等。
grandparent-pom.xml (spring-boot-starter-parent)
├─ dependencyManagement: [spring-web:6.1, spring-data:3.2, ...]
├─ pluginManagement: [spring-boot-maven-plugin:3.2.0, ...]
└─ properties: <java.version>17</java.version>
parent-pom.xml (company-microservice-parent)
├─ parent: spring-boot-starter-parent
├─ dependencyManagement:
│ └─ import spring-cloud-dependencies (2023.0.0)
└─ pluginManagement:
└─ 配置 dockerfile-maven-plugin
child-pom.xml (order-service)
├─ parent: company-microservice-parent
├─ dependencies:
│ ├─ spring-boot-starter-web (自动获得版本 6.1)
│ └─ spring-cloud-starter-gateway (自动获得版本 4.1)
└─ plugins:
└─ spring-boot-maven-plugin (自动获得 docker 配置)
整个流程解析简化如下。
order-service声明parent,继承所有版本清单- 引入
spring-boot-starter-web时,Maven 从dependencyManagement查到版本 - 打包时,
spring-boot-maven-plugin的配置从pluginManagement继承
最佳实践
不少企业内部,尤其是小企业,可能没有这些三段式的架构规约。
我本人摸滚打爬了多年,经过多年实践,推荐大家采用祖-父-子三代 POM 管理或约束模式。
第一代,约定官方 Parent
公司级根 POM,或者说是祖 POM。
<!-- 公司级根 POM -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
</parent>
第二代,公司级 BOM
公司级 BOM,也被内部称为父 POM。
<!-- company-bom/pom.xml -->
<artifactId>company-dependencies</artifactId>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<!-- Spring Cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2023.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 自研组件 -->
<dependency>
<groupId>com.company</groupId>
<artifactId>common-security</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<builder>paketobuildpacks/builder:base</builder>
</image>
</configuration>
</plugin>
</plugins>
</pluginManagement>
第三代,业务项目
业务项目,就是最后的子,受前两代弱约束。
<!-- order-service/pom.xml -->
<parent>
<groupId>com.company</groupId>
<artifactId>company-dependencies</artifactId>
<version>1.0.0</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.company</groupId>
<artifactId>common-security</artifactId>
</dependency>
</dependencies>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
这样做的收益简单总结如下。
- 版本集中管控,一处修改,全公司生效
- 业务项目极度简洁,专注业务依赖
- 构建配置标准化,避免每个团队重复踩坑
我不知道有多少网友是这样做的,或许也有不少人理解不了这样做的好处,也没关系。随着工作年限的增加,从工程化、架构设计等角度来思考,会有明白的那一天的。
Spring Cloud 为什么抛弃 starter-parent?
原本上面的一切都很好,但是 Spring Cloud 出现了“叛逆”,它抛弃了 starter-parent。
正如我的这篇文章《https://mp.weixin.qq.com/s/ZzfF025vRQkilz94W0T5tQ》中所说,Spring Cloud 移除了spring-cloud-starter-parent,并不在推荐使用它,转而拥抱了spring-cloud-dependencies。
注意,下面这个用法已经被抛弃了。
<!-- 旧的用法 -->
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>2024.0.0</version>
</parent>
究其原因,就是spring-cloud-starter-parent有它的局限性。
- Parent 只能有一个:一旦用了 Cloud 的 parent,就无法再用公司自定义的 parent
- Cloud 与 Boot 版本强绑定:Cloud parent 内部指定了 Boot 版本,升级不灵活
- 生态割裂:Netflix、Alibaba 等组件需要额外的 dependencyManagement 导入,配置分散
新范式纯 BOM 化设计
现在 Spring Cloud 官方推荐下面这样的使用方式。
<!-- 现代配置(推荐) -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.0</version>
</parent>
<dependencyManagement>
<dependencies>
<!-- Spring Cloud 作为 BOM 导入 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2024.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Cloud Alibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2024.0.0.0-RC1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
为什么要拥抱spring-cloud-dependencies?这就是进步?
哎,你别说。这种做,有以下 4 种好处。
- 单一职责:Boot 管框架,Cloud 管分布式,职责清晰
- 组合灵活:可同时导入多个 Cloud 生态的 BOM(Alibaba、Azure、AWS)
- 升级友好:Boot 和 Cloud 版本可独立迭代,互不干扰
- 符合 Maven 哲学:parent 用于继承“行为”,BOM 用于管理“版本”
官方原话是这样说的。
spring-cloud-starter-parentwas designed for a simpler era. Modern microservices require composable dependency management, not hierarchical inheritance.
翻译过来就是,spring-cloud-starter-parent是为更简单的时代而设计的。现代微服务需要的是可组合的依赖管理方式,而非层级继承。说白了,这里更能体现组合优于继承。
注意,上面有一个<scope>import</scope>。它是 Maven 2.0.9+ 的特性,它允许“导入”另一个 POM 的dependencyManagement段,效果相当于复制粘贴,合并多个dependencyManagement。这是实现 BOM 组合的关键。
版本号的覆盖优先级应该都懂吧。子项目显式指定的 version > 第二代 POM 的 version > 第一代 parent 的 version。
注意,千万不要在 dependencyManagement 中写 <scope>compile</scope>。因为这会强制所有子项目都必须引入该依赖,失去了“按需声明”的灵活性,违背了 BOM 的设计初衷。
总结
这一切都是架构演进背后的架构思维,祖-父-子三代 POM 管理模式,是老外总结的最有解,推荐大家使用。
另外,从spring-cloud-starter-parent的退场,我们看到了从继承到组合的架构范式转变。
| 模式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Parent 继承 | 简单直接 | 僵化、不可组合 | 单体应用、小型项目 |
| BOM 组合 | 灵活、可扩展 | 配置稍复杂 | 微服务、多生态集成 |
记住,记住一句话,好的依赖管理,不是让配置变少,而是让变化可控。

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号:xttblog2。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!
本文原文出处:业余草: » Spring Cloud 官宣分手 starter-parent,详解 BAT 都在用的“祖-父-子” 3 代模式