22、版本目录(Version Catalog):libs.versions.toml、集中管理依赖版本、多项目共享

说实话,Gradle 项目里最让人头疼的事情之一,就是依赖版本的管理。

我记得刚接手一个老项目时,打开 build.gradle 文件,好家伙,密密麻麻的依赖声明。同一个 Guava 版本,在三个子模块里写了三遍。升级版本?那得全局搜索替换,改漏一个就等着编译报错吧。

后来 Gradle 7.0 正式推出了 Version Catalog,也就是版本目录。这玩意儿说白了,就是给你一个集中存放依赖版本的地方。所有模块都从这个目录里读取,版本统一,升级方便。我个人习惯,现在新建项目第一件事就是配好 libs.versions.toml

什么是 Version Catalog?

Version Catalog 是 Gradle 提供的一种依赖版本管理机制。它把依赖的 group、name、version 抽离到一个单独的 TOML 文件中。这个文件通常叫 libs.versions.toml,放在项目的 gradle 目录下。

你想想看,以前我们写依赖是这样的:

implementation 'com.google.guava:guava:31.1-jre'

用了 Version Catalog 之后,变成这样:

implementation libs.guava

清爽多了吧?版本号只在一个地方维护,所有模块共享。

libs.versions.toml 文件结构

这个 TOML 文件分为三个主要区域:[versions][libraries][plugins]。我画了一张图,帮你快速理解它的结构:

libs.versions.toml 结构 [versions] guava = "31.1-jre" spring = "5.3.20" junit = "5.8.2" retrofit = "2.9.0" [libraries] guava = { group = "com.google.guava", name = "guava", version.ref = "guava" } spring-core = { ... } [plugins] spring-boot = { id = "org.springframework.boot", version = "2.7.0" } 版本号定义在 [versions] 中 依赖声明在 [libraries] 中引用版本 插件声明在 [plugins] 中 build.gradle 中使用:implementation libs.guava

如何定义版本和依赖

我们来看一个完整的 libs.versions.toml 示例:

[versions]
guava = "31.1-jre"
spring-boot = "2.7.0"
junit-jupiter = "5.8.2"
retrofit = "2.9.0"
okhttp = "4.9.3"

[libraries]
guava = { group = "com.google.guava", name = "guava", version.ref = "guava" }
spring-boot-starter-web = { group = "org.springframework.boot", name = "spring-boot-starter-web", version.ref = "spring-boot" }
spring-boot-starter-test = { group = "org.springframework.boot", name = "spring-boot-starter-test", version.ref = "spring-boot" }
junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junit-jupiter" }
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }

[plugins]
spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot" }

这里有个细节要注意:version.ref 引用了 [versions] 中定义的版本号。这样改版本时,只需要改 [versions] 里的值,所有引用它的依赖都会同步更新。

💡 我的习惯:版本号命名尽量简洁且语义化。比如用 guava 而不是 guavaVersion,用 spring-boot 而不是 springBootVersion。这样在 libs.guava 调用时,命名更自然。

在 build.gradle 中使用 Version Catalog

配置好 TOML 文件后,在 build.gradle 中就可以直接引用了:

dependencies {
    implementation libs.guava
    implementation libs.spring.boot.starter.web
    testImplementation libs.junit.jupiter
    implementation libs.retrofit
    implementation libs.okhttp
}

是不是很直观?libs 是 Gradle 自动生成的访问器。它会根据 TOML 文件中的 [libraries] 块,自动生成对应的属性方法。

对于插件,在 plugins 块中这样使用:

plugins {
    alias libs.plugins.spring.boot
}

注意这里用的是 alias 关键字,而不是 id。这是 Gradle 专门为 Version Catalog 提供的语法。

多项目共享依赖版本

在多模块项目中,Version Catalog 的优势更加明显。你只需要在根项目的 gradle/libs.versions.toml 中定义一次,所有子模块都能共享。

举个例子,假设你有三个子模块:corewebapi。每个模块都需要 Guava 和 Spring Boot。以前你需要在三个 build.gradle 里分别写版本号。现在只需要在根目录的 TOML 文件中定义一次,每个子模块直接引用 libs.guava 即可。

// core/build.gradle
dependencies {
    implementation libs.guava
}

// web/build.gradle
dependencies {
    implementation libs.guava
    implementation libs.spring.boot.starter.web
}

// api/build.gradle
dependencies {
    implementation libs.guava
    implementation libs.retrofit
}

升级版本时,只需要修改 TOML 文件中的版本号,所有模块自动生效。我曾经在一个 20 多个模块的项目里,用这个方式把 Spring Boot 从 2.6 升级到 2.7,只改了一行代码,编译全部通过。要是以前,我得逐个模块检查,至少得花半天时间。

版本目录的命名规范

Gradle 会自动将 TOML 文件中的 key 转换为驼峰命名。比如:

TOML 中的 key 生成的访问器
spring-boot-starter-web libs.spring.boot.starter.web
junit-jupiter libs.junit.jupiter
my-library libs.my.library

命名时尽量保持简洁,避免过长的 key。我个人习惯用 - 分隔单词,这样生成的访问器可读性更好。

避坑指南

⚠️ 我曾经踩过的坑:
  • TOML 文件位置必须正确:必须放在 gradle/libs.versions.toml,不能放在项目根目录或其他位置。否则 Gradle 找不到。
  • 版本号不要硬编码在 [libraries] 中:虽然可以直接写 version = "31.1-jre",但这样就失去了集中管理的意义。建议统一使用 version.ref 引用。
  • 插件版本也要集中管理:很多同学只管理依赖版本,忽略了插件版本。其实插件版本也应该放在 [versions] 中统一管理。
  • 不要混用旧语法:一旦使用了 Version Catalog,就不要再在 build.gradle 中直接写版本号了。混用会导致维护混乱。

进阶用法:多目录与条件依赖

对于大型项目,一个 TOML 文件可能不够用。Gradle 支持通过 settings.gradle 配置多个版本目录:

dependencyResolutionManagement {
    versionCatalogs {
        libs {
            from(files("gradle/libs.versions.toml"))
        }
        testLibs {
            from(files("gradle/test-libs.versions.toml"))
        }
    }
}

这样你就可以在子模块中分别使用 libstestLibs 两个目录。不过我个人建议,除非项目特别大,否则一个 TOML 文件就够了。文件多了反而不好管理。

📌 核心要点总结:
  • Version Catalog 通过 libs.versions.toml 集中管理依赖版本
  • 文件分为 [versions][libraries][plugins] 三个区域
  • 使用 libs.xxxbuild.gradle 中引用依赖
  • 多模块项目共享同一个版本目录,升级版本只需改一处
  • 插件版本也要纳入版本目录管理

好了,关于 Version Catalog 的核心内容就这些。说白了,它就是 Gradle 官方给你提供的一个依赖版本管理工具,用起来简单,效果却很好。如果你还在手动管理版本号,我强烈建议你试试这个方式。相信我,一旦用上,你就回不去了。


公众号:蓝海资料掘金营,微信deep3321