在Gradle中优雅地统一管理依赖
前言
不知大家在使用 Gradle 的时候是如何统一管理整个项目所需依赖的版本呢?尤其是在有多个模块的情况下。
曾经我写过 一篇文章 来介绍如何在 buildSrc
中使用常量来管理版本,但是实际上那种做法并不怎么优雅,甚至有些邪道。
当然,我自己事后也很快发现了问题,并没有真的那么继续下去。
那么现在怎么比较优雅的去对版本进行统一管理呢?分享 一个你也许已经知道、我自己也一直在用的方法。也算对之前的小文章简单的纠个错
使用 libs.versions.toml
这是 Gradle 在大约 8.x 版本左右开始支持的依赖管理方案(起始时间记不太清了,也可能更早),至少在 8.x 版本左右(或者说最近据我个人的观察)开始变得常见、稳定、流行了起来。
首先,你可以前往 官方文档 来了解 Version Catalog 详细的使用方式,而这篇小文章就以上手应用的方式简单为你介绍一下。
配置文件
首先,在你 Gradle 项目的根目录,在 gradle
目录中创建一个 toml 文件 libs.versions.toml
:
根目录
|
\- gradle
| \- libs.versions.toml <==== 在这里
|
\- src
\- build.gradle.kts
\- settings.gradle.kts
\- 其他什么别的东西
文件内容大概是这样样子的:
[versions]
kotlin = "2.0.0"
kotlinx-coroutines = "1.8.1"
log4j = "2.23.1"
ksp = "2.0.0-1.0.22"
[libraries]
# jetbrains-annotation
jetbrains-annotations = "org.jetbrains:annotations:24.1.0"
# kotlinx-coroutines
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
kotlinx-coroutines-reactor = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-reactor", version.ref = "kotlinx-coroutines" }
kotlinx-coroutines-debug = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-debug", version.ref = "kotlinx-coroutines" }
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" }
# log4j
log4j-api = { group = "org.apache.logging.log4j:log4j-api", version.ref = "log4j" }
log4j-core = { group = "org.apache.logging.log4j:log4j-core", version.ref = "log4j" }
log4j-slf4j2 = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version.ref = "log4j" }
[bundles]
log4j2 = ["log4j-api", "log4j-core", "log4j-slf4j2"]
[plugins]
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
可以看到配置文件中分了那么几段,它们分别代表着不同的作用。
[versions]
其实这几个分段看名字也能大概猜到它们的意思。
[versions]
即为定义某个东西的版本的,比如:
[versions]
kotlin = "2.0.0"
则代表定义了一个叫 kotlin
的东西的版本为 2.0.0
。
版本可以在后续的其他几个分类中引用使用,或者在配置文件里使用 libs.versions.xxx.get()
直接获取。
具体使用先按下不表,我们稍后再谈。
[libraries]
[libraries]
便是定义依赖的地方了。它有很多写法,比如直接使用字符串表示完整的依赖:
[libraries]
jetbrains-annotations = "org.jetbrains:annotations:24.1.0"
或者使用 group
、name
、version
完整的定义依赖坐标中的每一个部分:
[libraries]
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
kotlinx-coroutines-reactor = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-reactor", version.ref = "kotlinx-coroutines" }
又或者使用 module
和 version
将坐标与版本的信息分离开来:
[libraries]
log4j-api = { group = "org.apache.logging.log4j:log4j-api", version.ref = "log4j" }
log4j-core = { group = "org.apache.logging.log4j:log4j-core", version.ref = "log4j" }
log4j-slf4j2 = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version.ref = "log4j" }
细心的你也许发现了,如果使用 version.ref
而不是 version
,那便是对上面 [versions]
中定义的版本进行了一个引用。
当然,version
或 version.ref
也是可以省略的,这可以用于在配合使用其他 bom 或者别的什么来管理版本的时候使用,比如在配合 Spring Boot 时。
[bundles]
如果你在每一个地方都需要重复引用相同的多个 [libraries]
中的内容,那么不妨将它们打包在一起,一次引用它们全部 。[bundles]
便可用于将 [libraries]
中定义的一个或多个依赖包在一起。
[bundles]
log4j2 = ["log4j-api", "log4j-core", "log4j-slf4j2"]
[plugins]
[plugins]
也可以使用 version
或 version.ref
,并使用 id
来表示插件的ID信息。
它主要使用在配置 plugins {}
的时候。
[plugins]
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
使用
配置完成后,刷新/初始化你的Gradle,他会自动将上面的 信息编译,并可以让你在配置脚本中直接通过 libs
访问到它们。
versions
虽然 versions
通常是用来给其他部分做引用的,但是你依旧可以直接使用它们。
比如在 build.gradle.kts
中输出 kotlin
的版本信息的日志:
// 其他省略...
logger.info("Kotlin version = {}", libs.versions.kotlin.get())
可以看到,我们通过 libs.versions
访问到了其中的 kotlin
,并使用 get()
得到了它的字符串值。
libraries
接下来就是比较常见的 libraries
了。在 build.gradle.kts
中的 dependencies
中引入依赖的时候,不再使用字符串,而是直接用类型安全的 libs
所提供的属性即可。
// 其他省略...
dependencies {
compileOnly(libs.jetbrains.annotations)
implementation(libs.kotlinx.coroutines.core)
testImplementation(libs.kotlinx.coroutines.test)
}
细心的你可能又发现了,明明你定义的是 jetbrains-annotations
,怎么到这儿, -
就变成 .
了?
是的,从 libs.versions.toml
编译过来后就会将分段的命名使用 .
作为衔接。
你可以在 这里 看到官方对这种规则的详细描述。
bundles
一个“包体”的使用方式其实跟 libraries
差不多,只不过多了一层 bundles
。
// 其他省略...
dependencies {
// ...
implementation(libs.bundles.log4j2)
}
plugins
plugins
只能在 plugins {}
中使用,并需要通过 alias
来引用它们。
plugins {
// ...
alias(libs.plugins.ksp)
}
在 buildSrc 中使用
不过,gradle/libs.versions.toml
默认情况下并不能在 buildSrc
目录中的代码中使用,不过好在想要能够使用也很简单。
在 buildSrc
目录下创建一个 settings.gradle.kts
文件,大概结构如下:
根目录
|
\- buildSrc
| \- src
| \- build.gradle.kts
| \- settings.gradle.kts <==== 在这里
\- gradle
| \- libs.versions.toml
|
\- src
\- build.gradle.kts
\- settings.gradle.kts
\- 其他什么别的东西
然后在其中加上:
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
就可以咯~
结尾
其实 Version Catalog 还有很多更加复杂、灵活的使用方式,本文只介绍了一些很常见也很简单的“家常”用法。如果你有什么其他观点或者更好的方案,也欢迎评论区友好交流,我们下次再见~