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

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

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

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 Spring Boot 概述

    • 章节导读:Spring Boot概述与核心理念
    • Spring Boot是什么
    • Spring Boot与Spring Framework的关系
    • 约定优于配置
  • 第2章 快速入门与第一个应用

    • 章节导读:快速入门与第一个应用
    • SpringApplication
    • 第一个Spring Boot应用
  • 第3章 起步依赖与版本管理

    • 章节导读:起步依赖与版本管理
    • 起步依赖
    • BOM版本管理
  • 第4章 自动配置原理

    • 章节导读:自动配置原理
    • 自动配置原理
    • AutoConfigurationImportSelector
    • 自动配置报告
  • 第5章 核心注解

    • 章节导读:核心注解
    • @SpringBootApplication
    • @EnableAutoConfiguration
    • @ConditionalOnClass
    • @ConditionalOnMissingBean
    • @ConditionalOnBean
    • @ConditionalOnProperty
  • 第6章 外部化配置与属性绑定

    • 章节导读:外部化配置与属性绑定
    • 外部化配置
    • @ConfigurationProperties
    • @Value
    • 配置属性优先级
  • 第7章 Profile 与环境切换

    • 章节导读:Profile与环境切换
    • @Profile
    • 多环境配置文件
  • 第8章 内嵌服务器与部署

    • 章节导读:内嵌服务器与部署
    • 内嵌服务器
    • Fat Jar
  • 第9章 Actuator 与监控

    • 章节导读:Actuator与监控
    • Actuator Health
    • Actuator Info
    • Actuator Metrics
    • 自定义Endpoint
    • 自定义HealthIndicator
  • 第10章 开发工具与最佳实践

    • 章节导读:开发工具与最佳实践
    • Banner自定义
    • 热部署

一句话定位:spring-boot-dependencies BOM(Bill of Materials)是 Spring Boot 版本管理的中央锁,通过 Maven 的 <dependencyManagement> 机制统一锁定所有 Starter 及其传递依赖的版本号,让开发者无需手写 version 即可避免依赖冲突。


定义与作用

BOM(Bill of Materials,物料清单) 是 Maven 的一种依赖管理技术。spring-boot-dependencies 是 Spring Boot 官方提供的 BOM,它内部声明了数百个依赖的 <version>,覆盖 Spring Boot 生态中所有常用的库和框架。

当项目的 pom.xml 继承 spring-boot-starter-parent(或导入 spring-boot-dependencies)后,引入任何被 BOM 管理的依赖时,无需再写 <version>,Maven 会自动从 BOM 中获取对应的版本号。

它解决的痛点:

传统 Maven 项目使用 BOM 后
手动指定 spring-webmvc、jackson、tomcat、hikaricp 等版本,版本冲突频繁引入 spring-boot-starter-parent,所有版本自动锁定
升级 Spring 版本时,逐个修改 pom.xml 中的 10+ 个版本号只改父 POM 的 <version>,全部依赖同步升级
新成员引入依赖时不知道该用哪个版本,经常引入不兼容版本直接省略 <version>,BOM 保证兼容性
依赖树中出现两个不同版本的同一个库(如 jackson 2.12 和 2.13),导致 NoSuchMethodErrorBOM 统一管理,传递依赖自动对齐到同一版本

适用位置与常用属性

BOM 管理不是注解,而是 Maven 构建机制。它通过以下两种方式融入项目:

方式一:继承父 POM(推荐,本教程采用)

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.18</version>
    <relativePath/>
</parent>

方式二:导入 BOM(公司有自定义父 POM 时使用)

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.7.18</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

父 POM 提供的额外能力

能力说明是否可通过方式二获得
统一依赖版本管理spring-boot-dependencies BOM✅ 可以
spring-boot-maven-plugin 默认绑定打包 Fat JAR 无需额外配置插件❌ 需要手动配置
默认编码 UTF-8<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>❌ 需要手动配置
默认 Java 版本与 Spring Boot 版本对应的默认 JDK❌ 需要手动配置
资源过滤application.yml 中的 ${...} 占位符替换❌ 需要手动配置
依赖插件版本管理maven-compiler-plugin、maven-surefire-plugin 等版本锁定❌ 需要手动配置

核心原理

BOM 版本锁定机制

机制解读:

  1. 父 POM 继承:spring-boot-starter-parent 是 spring-boot-dependencies 的父 POM,它将 BOM 引入到项目中。
  2. <dependencyManagement> 锁定:BOM 在 <dependencyManagement> 中声明了每个依赖的 version 和 scope,这不会直接把依赖加入项目,而是定义了一个版本规则。
  3. <dependencies> 生效:当小崔在 <dependencies> 中引入 spring-boot-starter-web 时,Maven 发现没有 <version>,于是去 <dependencyManagement> 查找,找到 BOM 中锁定的 2.7.18,自动填充。
  4. 传递依赖对齐:Starter 的传递依赖(如 spring-webmvc 依赖 spring-core)也会从 BOM 中获取版本,确保整个依赖树版本一致。

版本管理层次图

优先级:开发者在 pom.xml 中显式写的 <version> 优先级最高,会覆盖 BOM 的锁定;BOM 的锁定优先级次之;如果两者都没有,Maven 会尝试从中央仓库获取最新版本(通常不可预期,生产环境严禁)。


完整示例

场景说明

飞翔科技的学生成绩管理系统使用 Spring Boot 2.7.18。小崔需要理解:为什么他的 pom.xml 可以不写 version?如果想升级版本,只需要改哪里?白歌让他做一个对比实验。

操作前:无父 POM 的 Maven 项目(手动管理版本)

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>

    <!-- 没有 parent,没有 BOM -->
    <groupId>com.feixiang</groupId>
    <artifactId>student-management</artifactId>
    <version>1.0.0</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.27</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.13.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>9.0.75</version>
        </dependency>
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>4.0.3</version>
        </dependency>
    </dependencies>
</project>

问题:

  • 每个依赖都需要手动写 version。
  • spring-webmvc 的传递依赖(如 spring-core)的版本由 Maven 自行解析,可能与 spring-webmvc 不兼容。
  • 升级时逐个修改版本号,容易遗漏。

使用父 POM 继承的完整代码

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>

    <!-- 继承 Spring Boot 父 POM,一键获得版本管理 + 插件绑定 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.18</version>
        <relativePath/>
    </parent>

    <groupId>com.feixiang</groupId>
    <artifactId>student-management</artifactId>
    <version>1.0.0</version>

    <properties>
        <!-- 自定义属性:覆盖 BOM 中某个依赖的版本 -->
        <mysql.version>8.0.33</mysql.version>
    </properties>

    <dependencies>
        <!-- 无需 version,由父 POM 的 BOM 锁定 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
            <!-- 可以省略,因为 BOM 已锁定 8.0.33;
                 也可以显式覆盖,但推荐在 <properties> 中统一覆盖 -->
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 无需配置 version,父 POM 已绑定 spring-boot-maven-plugin -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

版本覆盖机制:

在 spring-boot-starter-parent 中,BOM 的版本号被定义为 Maven 属性。例如:

<!-- 这是父 POM 内部的定义,开发者不可见,但可通过属性覆盖 -->
<properties>
    <mysql.version>8.0.32</mysql.version>
    <jackson.version>2.13.5</jackson.version>
</properties>

小崔可以在自己的 pom.xml 中通过同名属性覆盖:

<properties>
    <!-- 将 MySQL 驱动从 BOM 默认的 8.0.32 升级到 8.0.33 -->
    <mysql.version>8.0.33</mysql.version>
</properties>

注意:这是推荐的覆盖方式,而不是直接在 <dependency> 中写 <version>。通过属性覆盖,可以确保所有依赖该库的地方(包括传递依赖)都同步升级。

操作后运行结果及分析

执行 mvn dependency:tree -Dincludes=mysql 验证版本:

com.feixiang:student-management:jar:1.0.0
\- mysql:mysql-connector-java:jar:8.0.33:runtime

分析:

  1. 版本锁定生效:mysql-connector-java 的版本是 8.0.33,既不是 pom.xml 中显式写的(因为没写),也不是 Maven 中央仓库的最新版,而是 BOM 锁定后、又被 <mysql.version> 属性覆盖后的结果。
  2. 传递依赖对齐:spring-boot-starter-web 内部依赖的 jackson-databind、spring-webmvc、tomcat-embed-core 等版本全部与 2.7.18 的 BOM 一致,无需手动干预。
  3. 插件绑定:spring-boot-maven-plugin 无需写 <version>,父 POM 已绑定。执行 mvn package 会自动打包为 Fat JAR(包含 BOOT-INF/classes 和 BOOT-INF/lib)。
  4. 升级路径:当 Spring Boot 发布 2.7.19 时,小崔只需修改父 POM 的 <version>,所有依赖和插件自动同步升级。

易错场景与面试考点

易错场景:公司有自定义父 POM,直接继承 spring-boot-starter-parent 导致冲突

飞翔科技内部有一个统一的父 POM com.feixiang:feixiang-parent:3.0.0,规定了公司级的插件配置、仓库地址和编码规范。小崔直接在他的项目中继承了 spring-boot-starter-parent,导致公司规范失效。

错误示范:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.18</version>
</parent>

后果:

  • 丢失了公司父 POM 中定义的 <distributionManagement>(私服上传地址)。
  • 丢失了公司统一的 maven-checkstyle-plugin 配置。
  • 公司架构组要求所有项目必须继承 feixiang-parent,小崔的项目无法通过 CI 流水线检查。

正确做法:使用 BOM 导入(方式二),而不是继承父 POM(方式一)。

<parent>
    <groupId>com.feixiang</groupId>
    <artifactId>feixiang-parent</artifactId>
    <version>3.0.0</version>
</parent>

<dependencyManagement>
    <dependencies>
        <!-- 导入 Spring Boot BOM,获得版本锁定 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.7.18</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

<build>
    <plugins>
        <!-- 手动绑定 spring-boot-maven-plugin -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>2.7.18</version>
        </plugin>
    </plugins>
</build>

关键区别:import scope 的 BOM 只导入 <dependencyManagement> 中的版本定义,不会继承父 POM 的 <pluginManagement> 和属性。因此 spring-boot-maven-plugin 需要手动写 version(或在公司父 POM 中绑定)。

面试考点

Q:BOM 是什么?spring-boot-starter-parent 和 spring-boot-dependencies 有什么区别?

BOM(Bill of Materials)是 Maven 的依赖管理技术,通过 <dependencyManagement> 统一锁定依赖版本。spring-boot-dependencies 是纯 BOM,只包含版本锁定;spring-boot-starter-parent 是完整父 POM,继承它不仅能获得 BOM 的版本锁定,还能获得默认的插件绑定(spring-boot-maven-plugin)、编码设置(UTF-8)、资源过滤等构建能力。如果公司已有自定义父 POM,应使用 import scope 导入 spring-boot-dependencies,而非继承 spring-boot-starter-parent。

Q:如何覆盖 BOM 中某个依赖的版本?

推荐方式:在 pom.xml 的 <properties> 中定义与 BOM 中同名的属性。例如 BOM 内部定义了 <mysql.version>8.0.32</mysql.version>,在自己的 pom.xml 中写 <properties><mysql.version>8.0.33</mysql.version></properties> 即可覆盖。这种方式确保所有依赖该库的地方(包括传递依赖)都同步升级。不推荐直接在 <dependency> 中写 <version>,因为它只影响当前依赖声明,不保证传递依赖对齐。

Q:spring-boot-maven-plugin 的作用是什么?如果不继承父 POM,如何配置它?

spring-boot-maven-plugin 负责将 Spring Boot 应用打包为可执行的 Fat JAR。它将应用类和所有依赖 JAR 打包到一个 JAR 中,并通过自定义的 JarLauncher 类加载器处理 BOOT-INF/classes 和 BOOT-INF/lib 目录。如果不继承父 POM,需要在 <build><plugins> 中手动声明该插件并指定 version(与 Spring Boot 版本一致)。

Q:Maven 的 <dependencyManagement> 和 <dependencies> 有什么区别?

<dependencyManagement> 只定义版本约束,不会将依赖实际引入项目。它声明了"如果本项目或传递依赖中出现了这个依赖,就使用这个版本"。<dependencies> 才将依赖实际加入项目的 classpath。BOM 的精髓就在于:在 <dependencyManagement> 中统一锁定版本,在 <dependencies> 中按需引入依赖,两者分离,清晰可控。

上一页
起步依赖