在多模块的 Gradle 项目中,随着时间的推移,依赖管理往往会变得比较复杂。我注意到不同的子模块引用的第三方库版本容易出现不一致的情况,有时还会产生依赖冲突。
过去,我曾尝试过通过 ext 扩展属性(或 gradle.properties)加字符串拼接,或是使用 buildSrc 编写 Kotlin 脚本来集中管理版本。但它们都有一些局限性,例如缺乏代码提示,或者在 buildSrc 中修改一行代码会导致整个项目缓存失效重新编译。
后来我了解到,从 Gradle 7.4 开始,官方引入了 Gradle Version Catalog (版本目录)(即 libs.versions.toml)。最近,我尝试将一个项目迁移到了这种依赖管理方式,并在此记录一下我的实践过程。
什么是 Version Catalog?
我的理解是,Version Catalog 允许将所有的版本号、依赖库坐标(GAV)以及 Gradle 插件统一声明在一个独立的 .toml 文件中。然后在各个模块的 build.gradle / build.gradle.kts 中,可以以类型安全的方式去引用它们。
我观察到的优势:
- 统一控制源:全项目的依赖都集中在一个物理文件中,便于查阅和修改。
- IDE 体验:较好地支持了 IntelliJ IDEA 的代码补全和版本升级提示。
- 性能友好:相比
buildSrc方案,修改.toml文件通常不会导致整个 Gradle 构建逻辑的缓存失效。 - Bundle(依赖包)机制:可以将相关的几个库打包在一起,统一引入。
迁移过程记录
我以一个基于 Kotlin DSL (build.gradle.kts) 的 Spring Boot 多模块项目为例,记录了我的迁移步骤。
第一步:创建 libs.versions.toml
我在项目根目录下的 gradle 文件夹中,新建了一个名为 libs.versions.toml 的文件。
这个文件主要分为四个区块:
1 | # gradle/libs.versions.toml |
第二步:清理旧的属性定义
我移除了以前在 gradle.properties 或者根 build.gradle.kts 里定义过的零散版本号(如 springBootVersion=4.0.3)。
第三步:改造根项目(插件声明与版本提取)
在根目录的 build.gradle.kts 中,我将原有的字符串插件声明改为了 alias() 的方式。
修改前:
1 | plugins { |
修改后:
1 | plugins { |
如果在子模块闭包 subprojects { ... } 中需要用到版本号,我发现可以提前在闭包外提取出来:
1 | // 提前提取版本号供内部使用 |
第四步:改造子模块依赖声明
进入各个子模块后,我将硬编码的依赖替换为了 Catalog 引用。我注意到在 toml 文件中使用的 -(横杠)在 Kotlin DSL 中会被自动转换为 .(点)。
修改前:
1 | dependencies { |
修改后:
1 | dependencies { |
排查与学习:进阶技巧
与 Spring Dependency Management 的配合:
对于使用了 Spring BOM 的项目,我倾向于采用 Version Catalog + BOM 的组合。让 BOM 去管理它内置的框架依赖(如spring-boot-starter-web,jackson),剩余的第三方库再用toml统一管理。命名规范:
在toml文件中,我发现在命名时使用短横线-分隔单词(如spring-boot)比较好,因为 Gradle 在生成访问器时会自动将其转换为点号形式(libs.versions.spring.boot),在代码中引用时较为自然。自动化升级尝试:
由于toml是标准的配置文件格式,我尝试了将其与自动化工具(如 Dependabot 或 Renovate)结合。发现新版本发布时,工具可以直接解析并修改.toml文件,这使得依赖升级变得更加自动化。
总结反思
这次将项目迁移到 Gradle Version Catalog 的实践,帮助我更好地理解了现代 Gradle 的依赖管理机制。虽然初次迁移需要花费一些时间,但完成后确实提升了构建脚本的整洁度和类型安全性。这也提醒了我需要进一步了解 Gradle 缓存机制等更深层次的技术细节。