一箇中大型的 Java 專案往往包含若干 JAR 包,這些 JAR 包有著不同的版本號。如果這些 JAR 包單獨釋出,然後直接通過版本號引用相應的 JAR 包,不同版本的相容性維護將變得十分麻煩。為了解決這個問題,可以讓一個特殊的模組引用這些 JAR 包,將版本號定義在這個模組中,模組中的 JAR 都是相容的,對外發布時只發布這個特殊模組。這個特殊模組就是 BOM(Bill Of Materials)。
著名的 Spring Boot 就使用了這種方式來管理版本號,這個模組就是 spring-boot-dependencies,使用者在使用 Spring Boot Starter 相關依賴時引入特定版本的 spring-boot-dependencies,然後在引入其它依賴時只需要宣告 group 和 name 即可,不需要再指定版本號了。當然,在 Gradle 中使用 Spring Boot 外掛,或者在 Maven 中使用 spring-boot-starter-parent 作為父模組也能夠達到類似的效果。
本文將介紹如何通過 Gradle 來製作一個 BOM 以及如何在 Gradle 中使用 BOM。作為 Maven 中的一個概念,也可以使用 Maven 也可以製作和使用 BOM,但本文不涉及。
1. BOM 介紹
BOM (Bill Of Material) 是 Maven 倉庫中的一個概念,它本質也是一個可被引用的包,但不包含程式碼,只是宣告瞭一系列其它包。例如:Maven 中央倉庫中的 spring-boot-dependencies](https://repo1.maven.org/maven2/org/springframework/boot/spring-boot-dependencies/2.4.4/) 包。它只有一個 .pom
檔案。
下面是 Maven 官網上的一個簡單的 BOM 的 .pom
檔案:
<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.test</groupId>
<artifactId>bom</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<properties>
<project1Version>1.0.0</project1Version>
<project2Version>1.0.0</project2Version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>project1</artifactId>
<version>${project1Version}</version>
</dependency>
<dependency>
<groupId>com.test</groupId>
<artifactId>project2</artifactId>
<version>${project2Version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>parent</module>
</modules>
</project>
這個檔案宣告瞭兩個包(project1 和 project2)及其版本號,和一般 .pom 檔案中的宣告不同的是,
2. 使用 Gradle 製作一個 BOM
這裡我們假定要建立一個 BOM,用來統一管理三方 Java 包,其它業務模組通過引用這個 BOM 來間接引用需要使用的第三方 Java 包。工程完整程式碼:https://github.com/Robothy/gradle-bom-example
2.1 建立 BOM 工程
Gradle 中的 BOM 工程需要使用 java-platform
外掛,這樣的工程是一個不包含原始碼,只包含包宣告的特殊的元件,也被稱為平臺(platform)。
build.gradle 部分程式碼
plugins {
id 'java-platform'
}
dependencies {
constraints {
// 宣告一些三方包及其版本號
api "org.apache.kafka:kafka-clients:2.6.0"
api "redis.clients:jedis:3.5.2"
}
}
上面程式碼中,三方包的宣告沒有放在 dependencies 中,而是放在了 constraints 裡面。這表示如果使用了其中的包,優先使用 constraints 中宣告的版本。
BOM 專案中宣告包的方式有兩種:
-
api
表示包在編譯期可見。 -
runtime
表示包在執行期間可見。
2.2 BOM 的釋出
BOM 的釋出需要使用 maven-publish
外掛,其釋出配置如下:
publishing {
publications {
thirdPartPlatform(MavenPublication){
from components.javaPlatform
artifactId = "third-part-dependencies"
}
}
repositories {
mavenLocal()
}
}
BOM 的命名一般以 -dependencies
結尾,這裡我們取名為 third-part-dependnecies
。
執行 ./gradlew.bat publish
就可以將 BOM 釋出到本地的 Maven 倉庫了。釋出的 artifacts 包含兩個主要檔案(.pom 和 .module)和若干校驗檔案。其中 .pom 的檔案內容為 Maven 官方定義的 BOM 的標準格式,而 .module 檔案內容是 Gradle 描述後設資料的一種格式。
2.3 BOM 的使用
普通的 Java 應用或者 Java 庫使用 BOM 的時候需要先新增 BOM 依賴,然後使用其它的庫。例如:
// 引入 BOM
implementation platform("org.example:third-part-dependencies:1.0")
// 引入包,這時不需要再指定版本號
implementation "org.apache.kafka:kafka-clients"
當然,BOM 工程或者說 platform 工程也可以使用 BOM。
使用的時候需要在 dependencies 下面引入 BOM,然後在 constraints 下面宣告要使用的庫,宣告的時候無須指定版本。另外,需要在 configurations 中呼叫 javaPlatform.allowDependencies()
,否則會報錯。
configurations{
javaPlatform.allowDependencies()
}
dependencies {
api platform("org.springframework.boot:spring-boot-dependencies:2.4.4")
constraints {
api "org.apache.kafka:kafka-clients:2.6.0"
api "redis.clients:jedis:3.5.2"
api "org.springframework.batch:spring-batch-core"
}
}