本文目录导读:

这是一个非常标准且重要的 Java 工程实践,尤其是在 Maven 或 Gradle 构建的多模块项目中。
一句话解释:BOM(Bill of Materials,物料清单)就像一个“版本号字典”。 它只负责定义依赖的版本,但不负责引入依赖,所有子项目或使用方只需引用这个 BOM,就能保证关键依赖的版本完全一致,避免“依赖地狱”。
下面分 Maven 和 Gradle 两种主流构建工具,介绍实现方式。
Maven 方式(最经典、最普及)
Maven 通过 dependencyManagement 标签实现 BOM,通常需要创建一个专门的 POM 工程作为 BOM。
创建 BOM 模块 (e.g., my-bom)
这个模块只包含 POM 文件,没有 Java 代码,它的作用是统一定义版本。
<!-- my-bom/pom.xml -->
<project>
<groupId>com.example</groupId>
<artifactId>my-bom</artifactId>
<version>1.0.0</version>
<!-- 注意:打包方式必须是 pom -->
<packaging>pom</packaging>
<properties>
<!-- 将版本号抽取成属性,方便统一修改 -->
<spring.boot.version>3.2.0</spring.boot.version>
<jackson.version>2.16.0</jackson.version>
<lombok.version>1.18.30</lombok.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- 自家的核心库 -->
<dependency>
<groupId>com.example</groupId>
<artifactId>my-core-lib</artifactId>
<version>${project.version}</version> <!-- 和BOM版本同步 -->
</dependency>
<!-- 管理第三方依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- 管理 Spring Boot 全家桶,也可以直接 import 其他 BOM -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 管理 Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
关键点:
packaging是pom,并且使用dependencyManagement包裹。
子模块引用 BOM (e.g., my-service)
子模块不需要再写版本号,BOM 会自动带出。
<!-- my-service/pom.xml -->
<project>
<parent>
<!-- 如果你的项目有统一的父POM,可以继承 -->
<groupId>com.example</groupId>
<artifactId>my-parent</artifactId>
<version>1.0.0</version>
<relativePath>../my-parent/pom.xml</relativePath>
</parent>
<!-- 更推荐的方式:使用 scope=import,不依赖父POM -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 使用自家核心库,不需要写版本号 -->
<dependency>
<groupId>com.example</groupId>
<artifactId>my-core-lib</artifactId>
<!-- 版本号由 BOM 提供,这里可以省略 -->
<!-- <version>1.0.0</version> -->
</dependency>
<!-- 使用 Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<!-- 不写版本 -->
</dependency>
<!-- 使用 Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<!-- 不写版本 -->
</dependency>
</dependencies>
</project>
核心优势: 子模块只需要写
groupId和artifactId,版本完全由 BOM 控制,BOM 升级一次,所有子模块(只要不重写版本号)自动升级。
Gradle 方式(更现代、更灵活)
Gradle 原生支持 BOM,语法更简洁,推荐使用 platform 插件或直接 import。
定义 BOM 文件(通常是 build.gradle 或 build.gradle.kts)
// my-bom/build.gradle.kts (Gradle Kotlin DSL)
plugins {
`java-platform` // 声明这是一个平台(BOM)
`maven-publish` // 如果需要发布
}
javaPlatform {
// 允许定义依赖约束
allowDependencies()
}
dependencies {
// 定义约束:这是 BOM 的核心 API
constraints {
api("com.example:my-core-lib:1.0.0")
// 管理第三方库
api("com.fasterxml.jackson.core:jackson-databind:2.16.0")
api("org.projectlombok:lombok:1.18.30")
}
// 如果需要引入其他 BOM(如 Spring Boot)
api(platform("org.springframework.boot:spring-boot-dependencies:3.2.0"))
}
子模块使用 BOM
// my-service/build.gradle.kts
dependencies {
// 引入 BOM (使用 platform 关键字)
implementation(platform("com.example:my-bom:1.0.0"))
// 使用自家核心库 (版本由 BOM 提供)
implementation("com.example:my-core-lib")
// 使用 Jackson (版本由 BOM 提供)
implementation("com.fasterxml.jackson.core:jackson-databind")
// 使用 Lombok (版本由 BOM 提供,编译时注解处理器)
annotationProcessor("org.projectlombok:lombok")
// 如果某个子模块需要特殊覆盖版本,可以直接写版本,此时子模块的版本优先
// implementation("com.fasterxml.jackson.core:jackson-databind:2.14.0")
}
Gradle 特殊语法:
platform(...):强制使用 BOM 定义的版本。enforcedPlatform(...):强制覆盖所有版本(不推荐,容易冲突)。
为什么推荐统一管理?
- 消除版本冲突:避免 A 依赖 Jack 1.0,B 依赖 Jack 2.0,Jar 冲突。
- 简化升级:升级版本时,只需修改 BOM 文件的一个数字,所有模块自动沿用新版本。
- 团队规范:架构组或技术委员会可以发布一个“公司级 BOM”,所有人必须遵守,统一技术栈。
- 与 Spring Boot 对齐:Spring Boot 本身就是一个 BOM,你可以在自己的 BOM 中
import它,再添加公司私有库。
最佳实践与注意事项
- 不要滥用:BOM 只适合管理需要强制统一的依赖(如 JSON 库、日志门面、Spring 核心等),对于功能强大但版本敏感度低的库(如
commons-io),可以视情况而定。 - 优先级规则:
- 子模块中显式声明的版本号,优先级高于 BOM 定义的版本。
- BOM A 和 BOM B 定义了同一个库的不同版本,谁先声明谁生效(Maven 是“最先声明者优先”原则)。
- 发布:BOM 本身也是一个构件(Artifact),应该发布到私有仓库(Nexus/JFrog),并被所有子项目引用。
- 独立发布:BOM 应该作为一个独立的模块发布,版本号可以独立于业务模块,这样 BOM 升级时业务模块可以选择性升级。
| 方式 | 核心机制 | 适用场景 | 版本优先级 |
|---|---|---|---|
| Maven BOM | dependencyManagement + scope=import |
传统 Java EE 项目、微服务 | 子模块显式版本 > BOM |
| Gradle BOM | java-platform + platform() |
新项目、灵活度高的项目 | 子模块显式版本 > BOM |