Gradle 多模块依赖集中管理方案,Version Catalogs 详解(Kotlin DSL)

一、认识 Version Catalogs?

1. 什么是 Version Catalogs?

Version Catalogs 是 Gradle 官方推荐的集中式依赖版本管理方案 ,用于统一管理项目中所有依赖的版本信息------通过 TOML 格式的配置文件(默认是 libs.versions.toml),将项目中的依赖版本、库、插件及依赖分组进行结构化管理,最终实现版本集中控制、依赖引用标准化。

这种管理机制的核心优势的是「一处定义,处处引用」:既避免了依赖声明中的版本硬编码,确保项目(尤其是多模块项目)的依赖版本全局统一,又简化了依赖引用流程,降低版本冲突风险,同时提升依赖管理的可读性与可维护性。

2. Gradle 版本支持

Version Catalogs 作为 Gradle 核心的依赖管理增强功能,于 Gradle 7.0 版本作为实验性功能引入,7.4 版本提升为稳定功能详见 Gradle 7.4 发布说明),并在 Gradle 8.0+ 版本中进一步优化完善,成为官方推荐的多模块项目依赖管理方案,因此项目必须使用 Gradle 7.4 或更高版本

运行命令检查当前 Gradle 版本:

bash 复制代码
./gradlew --version

二、Version Catalogs 配置文件详解

1. 默认配置规范

Gradle 遵循「约定优于配置」原则,对 Version Catalog 配置文件有以下明确约定:

  • 文件名要求 :配置文件以 libs.versions.toml 命名
  • 文件路径 :存放于项目根目录下的 gradle 文件夹中,完整路径为:根项目/gradle/libs.versions.toml

即 Gradle 默认会读取项目根目录下的 gradle/libs.versions.toml 文件。

plaintext 复制代码
your-project/
└── gradle/
    └── libs.versions.toml  # 默认 Version Catalog 配置文件

2. 配置文件核心结构

Version Catalog 的核心配置文件(libs.versions.toml)将项目的依赖版本、库、插件和依赖组进行结构化管理。按依赖管理逻辑依次分为四个核心配置节点(Tables):

部分 作用
[versions] 定义「版本变量」,可被依赖、插件引用
[libraries] 定义「依赖声明」(groupId + artifactId + 版本),版本可引用 [versions] 中的变量
[plugins] 定义「插件声明」(插件 ID + 版本),版本可引用 [versions] 中的变量
[bundles] 定义「依赖分组」(一组相关依赖的集合,方便一次性引入)
[versions]:版本号集中定义

[versions] 块将所有依赖 / 插件的版本号抽离为可复用的 "变量",供其他节点引用,确保版本统一。

定义格式如下:

toml 复制代码
[versions]
变量名 = "版本值"

例如:

toml 复制代码
[versions]
junit = "5.9.3"
spring-boot = "3.2.0"

也支持通配符(+)或 Maven 范围语法([ )),定义动态版本的版本号:

toml 复制代码
[versions]
kotlin = "1.9.+"  # 匹配 1.9.x 最新补丁版
guava = "32.+"    # 匹配 32.x 最新次要版
jackson = "[2.0.0,)"  # 匹配 2.0.0 及以上版本
[libraries]:依赖声明

[libraries] 节点用于定义具体依赖的 GAV 坐标(group, name, version)。

依赖声明格式如下:

toml 复制代码
[libraries]
# 1. 完整写法(推荐,清晰展示 group/name)
依赖名 = { group = "组ID", name = " artifactID", version = "版本" }

# 2. 简写写法(通过 module 合并 group:name)
依赖名 = { module = "group:name", version = "版本" }

# 3. 无版本写法(版本由 BOM 管理时)
依赖名 = { module = "group:name" }

# 4. 极简写法(直接拼接 group:name:version)
依赖名 = "group:name:version"

例如:

toml 复制代码
[libraries]
spring-boot-starter-web = { 
  group = "org.springframework.boot", 
  name = "spring-boot-starter-web", 
  version = "3.2.0"
}

版本号推荐通过 version.ref = "versions 中的变量名" 引用 [versions] 中的变量:

toml 复制代码
[libraries]
spring-boot-starter-web = { 
  group = "org.springframework.boot", 
  name = "spring-boot-starter-web", 
  version.ref = "spring-boot"  # 引用 [versions] 中的 spring-boot 版本
}
[plugins]:插件管理

[plugins] 节点用于集中定义项目使用的 Gradle 插件(包含第三方和官方插件),版本可引用 [versions] 变量。

配置格式如下:

toml 复制代码
[plugins]
# 1. 第三方插件(需指定 id 和版本)
插件名 = { id = "插件ID", version = "版本" }

# 2. 官方插件(通常无需版本)
插件名 = { id = "插件ID" }

例如:

toml 复制代码
[plugins]
spring-boot = { 
  id = "org.springframework.boot", 
  version.ref = "spring-boot"  # 引用 [versions] 中的版本
}
java = { id = "java" }  # 官方插件无需版本
[bundles]:依赖分组

[bundles] 节点用于将 [libraries] 中定义的多个相关依赖组合为 "依赖包",实现一次性引入。

配置格式如下:

toml 复制代码
[bundles]
分组名 = [ "依赖名1", "依赖名2", ... ]  # 依赖名对应 [libraries] 中的键

例如,在 [bundles] 中定义一个 JUnit5 的依赖包,包含测试时需要的依赖( junit-jupiter-api, junit-jupiter-enginejunit-platform-launcher):

toml 复制代码
[bundles]
junit5 = [
    "junit-jupiter-api",    # 引用 [libraries] 中的 junit-jupiter-api
    "junit-jupiter-engine", # 引用 [libraries] 中的 junit-jupiter-engine
    "junit-platform-launcher"
]

💡 提示: [bundles] 中引用的是 [libraries] 中定义的键(Key),而不是完整的坐标。

3. 在构建脚本中引用配置

Gradle 会自动解析 libs.versions.toml 并生成类型安全的 libs 对象 ------ 这是所有配置引用的核心入口,所有在 libs.versions.toml 中定义的版本、依赖、插件、分组,都会映射为 libs 对象的属性。

别名转换规则

Gradle 生成 libs 对象时会自动将 libs.versions.toml 中的别名(依赖名、插件名、分组名)转换为符合 Kotlin 风格的属性名,核心规则如下:

  1. 分隔符转换 :别名支持 -_. 分隔符,Gradle 会自动将其规范化为点式访问器;
  2. 大小写兼容 :TOML 键名无需刻意写驼峰(如 androidx-core-ktx),转换后自动适配 Kotlin 风格(libs.androidx.core.ktx);
  3. 唯一性校验:若转换后属性名重复,Gradle 会直接报错,只需调整 TOML 中的键名即可。
TOML 中的键名(示例) libs 对象中的属性名 说明
guava libs.guava 无分隔符,直接映射
androidx-core-ktx libs.androidx.core.ktx 连字符转点
dagger_core libs.dagger.core 下划线转点

💡 提示:命名转换是 Gradle 自动完成的类型安全映射,无需手动配置,掌握该规则能快速对应 TOML 配置与构建脚本中的引用路径,避免引用报错。

引用 [libraries] 依赖

[libraries] 中定义的每个依赖都会按上述规则映射为 libs.转换后的依赖名 属性,引用时直接在 dependencies 代码块中通过 libs.转换后的依赖名 引用即可,无需手动拼接 group:name:version

示例:

toml 复制代码
# libs.versions.toml
[libraries]
guava = "com.google.guava:guava:32.1.3-jre"
spring-boot-starter-web = "org.springframework.boot:spring-boot-starter-web:3.2.0"

引用 [libraries] 中的 guavaspring-boot-starter-web 依赖:

kotlin 复制代码
// build.gradle.kts
dependencies {
    implementation(libs.guava)
    implementation(libs.spring.boot.starter.web)
}

依赖名spring-boot-starter-web通过转换规则会转换为 libs 对象的 libs.spring.boot.starter.web 属性:

引用 [plugins] 插件

[plugins] 中的插件会映射为 libs.plugins.插件名,需通过 alias() 函数引用。

示例:

toml 复制代码
# libs.versions.toml
[plugins]
java = { id = "java" }  # Java 插件

build.gradle.kts 中通过 alias() 函数引用 [plugins] 中的 java 插件:

kotlin 复制代码
// build.gradle.kts
plugins {
    alias(libs.plugins.java)
}
引用 [bundles] 依赖分组

[bundles] 中的分组会映射为 libs.bundles.分组名,可一次性引入所有依赖。

示例:

toml 复制代码
# libs.versions.toml
[bundles]
junit5 = [
    "junit-jupiter-api",    # 引用 [libraries] 中的 junit-jupiter-api
    "junit-jupiter-engine", # 引用 [libraries] 中的 junit-jupiter-engine
    "junit-platform-launcher"
]

通过 libs.bundles.junit5 一次性引入 junit5 分组中的所有依赖:

kotlin 复制代码
// build.gradle.kts
dependencies {
    testImplementation(libs.bundles.junit5)
}
引用 [versions] 版本(不推荐)

[versions] 中的版本会映射为 libs.versions.变量名,通常仅在 TOML 内部引用,而非直接在构建脚本中使用:

kotlin 复制代码
// 不推荐直接使用,建议通过 [libraries]/[plugins] 间接引用
dependencies {
    testImplementation("org.junit.jupiter:junit-jupiter-api:${libs.versions.junit.get()}")
}

三、高级用法

1. 覆盖默认 Catalog 路径

如果不想使用默认的 gradle/libs.versions.toml,可以在 settings.gradle.kts 中配置自定义路径:

kotlin 复制代码
// settings.gradle.kts
dependencyResolutionManagement {
  versionCatalogs {
    // 默认 Catalog(名称仍为 libs)
    create("libs") {
      from(file("gradle/custom-versions.toml"))  // 自定义路径
    }
  }
}

Gradle 会优先加载 gradle/custom-libs.versions.toml,而非默认的 gradle/libs.versions.toml,且仍通过 libs 对象引用(命名空间不变)。

💡 提示 :Gradle 不支持通过 from(files()) 为同一个命名空间(如 libs)导入多个 TOML 文件 ------ 单个版本目录命名空间只能关联一个 TOML 文件。

2. 更改默认 Catalog 名称

如果默认目录名称 libs 与项目中其他扩展冲突,可修改默认目录名称:

kotlin 复制代码
// settings.gradle.kts
dependencyResolutionManagement {
    defaultLibrariesExtensionName = "projectLibs" // 新的默认目录名称
}

修改后,访问方式变为:

kotlin 复制代码
// build.gradle.kts
plugins {
    alias(projectLibs.plugins.java)
    alias(projectLibs.plugins.spring.boot)
}

dependencies {
    implementation(projectLibs.guava)
}

3. 覆盖 Catalog 的版本

导入 Catalog 后,可覆盖其中声明的版本,无需修改原目录文件:

kotlin 复制代码
// settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            from(files("gradle/custom-versions.toml")) // 导入 Catalog
            version("groovy", "3.0.6") // 覆盖 "groovy" 版本
        }
    }
}

所有引用 groovy 版本的依赖都会自动使用覆盖后的 3.0.6 版本。

💡 提示:覆盖仅影响依赖声明时使用的版本,实际解析的版本仍可能因冲突解决而变化。

4. 加载多个 Catalog 文件(多命名空间)

同时加载多个 Catalog 文件,为不同文件分配独立命名空间,实现依赖分组管理:

kotlin 复制代码
// settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        // 业务依赖目录
        create("businessLibs") {
            from(files("gradle/business.versions.toml"))
        }
        // 工具依赖目录(如构建工具、插件)
        create("toolLibs") {
            from(files("gradle/tools.versions.toml"))
        }
    }
}

不同的命名空间对应不同对象,如 businessLibstoolLibs,在构建脚本中分别访问:

kotlin 复制代码
// build.gradle.kts
dependencies {
    implementation(businessLibs.someDependency) // 业务依赖
    implementation(toolLibs.someTool) // 工具依赖
}

💡 提示 :为避免命名冲突,目录名称建议以 Libs 结尾(如 businessLibstestLibs)。

5. 多目录合并与优先级

如果导入多个目录且存在同名别名,后导入的目录会覆盖先导入的目录中的定义:

kotlin 复制代码
// settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        create("combinedLibs") {
            from("com.mycompany:base-catalog:1.0") // 基础目录
            from(files("gradle/custom-catalog.toml")) // 自定义目录(覆盖基础目录中的同名别名)
        }
    }
}

四、基于 version-catalog 插件发布版本目录(多项目共享)

如果需要在组织内部或外部共享版本目录(如公司内部所有项目复用一套依赖版本规范),Gradle 提供了 version-catalog 插件可将版本目录打包为独立组件发布,实现跨项目 / 团队的依赖配置共享。

1. 创建版本目录项目

为了版本目录的独立维护,建议创建一个空的 Gradle 项目 专门用于发布版本目录(也可在现有多模块项目中新增一个 catalog 子模块)。

项目目录结构:

plaintext 复制代码
catalog-project/
├── gradle/
│   └── wrapper/          # Gradle 包装器(确保统一 Gradle 版本)
├── build.gradle.kts      # 核心配置文件
├── settings.gradle.kts   # 项目设置
└── gradlew/gradle.bat    # Gradle 执行脚本

2. 定义版本目录并配置发布规则

应用插件

build.gradle.kts 中引入 version-catalog(构建版本目录)和 maven-publish(发布到仓库)插件:

kotlin 复制代码
// build.gradle.kts
plugins {
    // 用于生成可发布的版本目录组件
    `version-catalog`
    // 提供Maven仓库发布能力(必选,否则无法共享)
    `maven-publish`
}
配置项目元信息

定义项目的 Group、Version 和描述(将作为 Maven 工件的 GAV 坐标,必须唯一):

kotlin 复制代码
// build.gradle.kts
group = "com.shijie"
version = "1.0-SNAPSHOT"
description = "Shared Version Catalog for Company Projects"
定义版本目录内容

通过catalog扩展块声明版本、依赖、插件和捆绑包(对应 TOML 文件的四大核心区块):

kotlin 复制代码
// build.gradle.kts
catalog {
    versionCatalog {
        // 1. 版本变量(可被依赖/插件引用,便于统一升级)
        version("spring-boot", "3.2.0")
        version("junit", "5.9.3")
        version("kotlin", "1.9.22")

        // 2. 依赖库(group:name,版本可关联变量或直接指定)
        library("spring-boot-starter-web", "org.springframework.boot", "spring-boot-starter-web")
            .versionRef("spring-boot")  // 关联上面定义的spring-boot版本
        library("junit-jupiter", "org.junit.jupiter", "junit-jupiter-api")
            .versionRef("junit")
        library("lombok", "org.projectlombok", "lombok")
            .version("1.18.30")  // 直接指定版本(适用于较少变更的依赖)

        // 3. 插件(id:group:version)
        plugin("spring-boot", "org.springframework.boot", "3.2.0")

        // 4. 依赖捆绑包(将多个相关依赖组合,简化引用)
        bundle("spring-web", listOf("spring-boot-starter-web"))  // 包含web starters
        bundle("junit5", listOf("junit-jupiter"))  // 包含JUnit5核心依赖
    }
}
配置发布仓库

通过publishing块指定发布目标(本地仓库用于测试,私服用于团队共享):

kotlin 复制代码
// build.gradle.kts
publishing {
    publications {
        // 创建Maven格式的发布物,关联版本目录组件
        create<MavenPublication>("maven") {
            from(components["versionCatalog"])  // 核心:将版本目录打包为发布组件
        }
    }
    repositories {
        // 本地仓库(开发测试用,路径:~/.m2/repository)
        mavenLocal()
        
        // 企业私有仓库(如Nexus、Artifactory,团队共享用)
        maven {
            url = uri("https://your-company-nexus.com/repository/maven-releases/")
            // 仓库认证(根据私服配置填写)
            credentials {
                username = "your-username"
                password = "your-password"
            }
        }
    }
}

3. 发布版本目录到仓库

在版本目录项目根目录执行 Gradle 发布命令:

bash 复制代码
# 发布到本地Maven仓库(用于本地测试)
./gradlew publishToMavenLocal

# 发布到配置的私服(团队共享)
./gradlew publish

发布成功后,仓库中会生成以下工件(GAV:com.shijie:version-catalog:1.0-SNAPSHOT):

4. 其他项目引用发布的版本目录

settings.gradle.kts 中通过 dependencyResolutionManagement 引用版本目录:

kotlin 复制代码
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            // 引用发布的团队版本目录
            from("com.shijie:catalog-project:1.0-SNAPSHOT")
        }
    }

    repositories {
        mavenCentral()
        mavenLocal() // 本地仓库(测试用)
        maven { url = uri("https://your-nexus-url/repository/maven-releases/") }
    }
}

build.gradle.kts 中通过 libs 前缀引用共享的依赖和插件:

kotlin 复制代码
plugins {
    // 引用版本目录中的 spring-boot 插件
    alias(libs.plugins.spring.boot)
}

dependencies {
     // 引用版本目录中的依赖
    implementation(libs.spring.boot.starter.web)
}
相关推荐
Java小白笔记5 小时前
BigDecimal用法示例
java·开发语言·spring boot
czlczl200209255 小时前
双 Token 机制下的无感刷新(Refresh Token)后端实现
数据库·spring boot·redis·后端·mysql
程序员阿鹏5 小时前
OOM是如何解决的?
java·开发语言·jvm·spring
BoomHe5 小时前
Android 13 (API 33)开发自己的 Settings ,如何配置 WiFi BT 权限
android
城东米粉儿5 小时前
ConcurrentHashMap实现原理 笔记
android
宠..5 小时前
创建单选按钮控件
java·服务器·数据库
李慕婉学姐5 小时前
Springboot社会工作机构管理系统w19724cv(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
SimonKing5 小时前
JetBrains 重大变革:IDEA 2025.3 统一发行版发布,告别选择困难
java·后端·程序员
网安_秋刀鱼6 小时前
【java安全】shiro反序列化1(shiro550)
java·开发语言·安全·web安全·网络安全·1024程序员节