使用版本目录集中管理依赖项版本

使用版本目录提供了一种集中式的声明式方式来管理构建中的依赖项版本。

解释

当您在单一的共享版本目录中定义依赖项版本时,可以减少重复并简化升级。您只需在一个地方更新版本,而不必更改数十个 build.gradle(.kts) 文件。这简化了维护,提高了代码一致性,并降低了模块之间意外版本偏差的风险。跨项目的一致版本声明也使得在测试期间更容易推断行为——尤其是在模块化构建中,传递性升级可能会在构建后期悄无声息地改变运行时行为。

然而,版本目录仅影响声明的版本,而非解析后的版本。将它们与依赖锁定版本对齐结合使用,可以在构建之间强制保持一致性。要影响解析后的版本,请查看平台

示例

不要这样做

避免在 project.ext、常量或局部变量中声明版本

gradle/libs.versions.toml
[versions]
groovy = "3.0.5"
junit-jupiter = "5.10.0"

[libraries]
groovy-core = { module = "org.codehaus.groovy:groovy", version.ref = "groovy" }
groovy-json = { module = "org.codehaus.groovy:groovy-json", version.ref = "groovy" }
groovy-nio = { module = "org.codehaus.groovy:groovy-nio", version.ref = "groovy" }
commons-lang3 = { group = "org.apache.commons", name = "commons-lang3", version = { strictly = "[3.8, 4.0[", prefer="3.9" } }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }

[bundles]
groovy = ["groovy-core", "groovy-json", "groovy-nio"]

[plugins]
versions = { id = "com.github.ben-manes.versions", version = "0.45.0" }
build.gradle.kts
plugins {
    id("java-library")
    alias(libs.plugins.versions)
}
dependencies {
    api(libs.bundles.groovy)
    testImplementation(libs.junit.jupiter)
    implementation(libs.commons.lang3)
}
build.gradle
plugins {
    id('java-library')
    alias(libs.plugins.versions)
}
dependencies {
    api(libs.bundles.groovy)
    testImplementation(libs.junit.jupiter)
    implementation(libs.commons.lang3)
}

避免将版本目录用于不相关的目的

  • 不要用它们来存储共享字符串或非库常量

  • 不要用任意逻辑或特定插件配置来过度使用它们

而是这样做

在您的 gradle/ 目录中使用集中的 libs.versions.toml 文件

build.gradle.kts
plugins {
    id("java-library")
    id("com.github.ben-manes.versions").version("0.45.0")
}
val groovyVersion = "3.0.5"

dependencies {
    api("org.codehaus.groovy:groovy:$groovyVersion")
    api("org.codehaus.groovy:groovy-json:$groovyVersion")
    api("org.codehaus.groovy:groovy-nio:$groovyVersion")

    testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
}

dependencies {
    implementation("org.apache.commons:commons-lang3") {
        version {
            strictly("[3.8, 4.0[")
            prefer("3.9")
        }
    }
}
build.gradle
plugins {
    id('java-library')
    id('com.github.ben-manes.versions').version('0.45.0')
}
def groovyVersion = '3.0.5'

dependencies {
    api("org.codehaus.groovy:groovy:$groovyVersion")
    api("org.codehaus.groovy:groovy-json:$groovyVersion")
    api("org.codehaus.groovy:groovy-nio:$groovyVersion")

    testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")

    implementation("org.apache.commons:commons-lang3") {
        version {
            strictly("[3.8, 4.0[")
            prefer("3.9")
        }
    }
}

合理命名版本目录条目

在版本目录中使用一致且具有描述性的名称,可以提高构建脚本的可读性和可维护性。

解释

版本目录提供了一种集中管理依赖项的方式。采用清晰的目录条目命名约定,可以确保开发人员在整个项目中轻松识别和使用依赖项。

以下准则有助于有效命名目录条目

  1. 使用连字符分隔段落:优先使用连字符(-)而非下划线(_)来分隔条目名称的不同部分。

    示例:对于 org.apache.logging.log4j:log4j-api,使用 log4j-api

  2. 从项目组派生第一段:使用项目组 ID 中的唯一标识符作为第一段。

    示例:对于 com.fasterxml.jackson.core:jackson-databind,使用 jackson-databind

  3. 省略冗余段落:如果组 ID 和 artifact ID 相同,则避免重复它们。

    示例:对于 io.ktor:ktor-client-core,使用 ktor-client-core,而不是 ktor-ktor-client-core

  4. 将内部连字符转换为 camelCase:如果 artifact ID 包含连字符,将其转换为 camelCase 以提高代码的可读性。

    示例spring-boot-starter-web 变为 springBootStarterWeb

  5. 避免隐含术语:排除在项目上下文中显而易见或隐含的术语。

    示例:对于 com.amazonaws:aws-java-sdk-core,使用 aws-core,而不是 aws-javaSdkCore

  6. 为插件库添加 -plugin 后缀:当将插件作为库引用时(不在 [plugins] 部分),在名称后附加 -plugin

    示例:对于 org.owasp:dependency-check-gradle,使用 dependency-check-plugin

示例

gradle/libs.versions.toml
[versions]
slf4j = "2.0.13"
jackson = "2.17.1"
groovy = "3.0.5"
checkstyle = "8.37"
commonsLang = "3.9"

[libraries]
# SLF4J
slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }

# Jackson
jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
jackson-dataformatCsv = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-csv", version.ref = "jackson" }

# Groovy bundle
groovy-core = { module = "org.codehaus.groovy:groovy", version.ref = "groovy" }
groovy-json = { module = "org.codehaus.groovy:groovy-json", version.ref = "groovy" }
groovy-nio = { module = "org.codehaus.groovy:groovy-nio", version.ref = "groovy" }

# Apache Commons Lang
commons-lang3 = { group = "org.apache.commons", name = "commons-lang3", version = { strictly = "[3.8, 4.0[", prefer = "3.9" } }

[bundles]
groovy = ["groovy-core", "groovy-json", "groovy-nio"]

[plugins]
versions = { id = "com.github.ben-manes.versions", version = "0.45.0" }
build.gradle.kts
plugins {
    id("java-library")
    alias(libs.plugins.versions)
}

repositories {
    mavenCentral()
}

dependencies {
    // SLF4J
    implementation(libs.slf4j.api)

    // Jackson
    implementation(libs.jackson.databind)
    implementation(libs.jackson.dataformatCsv)

    // Groovy bundle
    api(libs.bundles.groovy)

    // Commons Lang
    implementation(libs.commons.lang3)
}
build.gradle
plugins {
    id 'java-library'
    alias(libs.plugins.versions)
}

repositories {
    mavenCentral()
}

dependencies {
    // SLF4J
    implementation libs.slf4j.api

    // Jackson
    implementation libs.jackson.databind
    implementation libs.jackson.dataformatCsv

    // Groovy bundle
    api libs.bundles.groovy

    // Commons Lang
    implementation libs.commons.lang3
}

settings.gradle.kts 中设置依赖仓库

settings.gradle.kts 中声明插件和依赖项的仓库。

解释

使用 settings.gradle.kts 文件声明仓库有以下几个好处

  • 避免重复:集中声明仓库消除了在每个项目的 build.gradle.kts 中重复声明的需要。

  • 提高可调试性:确保所有项目在解析期间都从相同的仓库以一致的顺序解析依赖项。

  • 匹配构建模型:仓库不是项目定义的一部分;它们是全局构建逻辑的一部分,因此 settings 是一个更合适放置它们的地方。

注意:尽管dependencyResolutionManagement.repositories 是一个孵化中的 API,但它是声明仓库的首选方式。

示例

不要这样做

您可以在单独的 build.gradle.kts 文件中设置仓库,使用

build.gradle.kts
buildscript {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
}

plugins {
    id("java")
}

repositories {
    mavenCentral()
}
build.gradle
buildscript {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
}

plugins {
    id("java")
}

repositories {
    mavenCentral()
}

而是这样做

而是,您应该在 settings.gradle.kts 中像这样设置它们

settings.gradle.kts
pluginManagement {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
    repositories {
        mavenCentral()
    }
}
settings.gradle
pluginManagement {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
    repositories {
        mavenCentral()
    }
}

不要显式依赖 Kotlin 标准库

Kotlin Gradle 插件会自动为每个源集添加对 Kotlin 标准库 (stdlib) 的依赖,因此无需显式声明它。

解释

添加的标准库版本与应用于项目的 Kotlin Gradle 插件版本相同。如果您的构建不需要特定或不同版本的标准库,则应避免手动添加它。

kotlin.stdlib.default.dependency 属性设置为 false 可以阻止 Kotlin 插件自动将 Kotlin 标准库依赖项添加到您的项目中。这在特定场景下很有用,例如当您希望手动管理 Kotlin 标准库依赖项版本时。

示例

不要这样做

build.gradle.kts
plugins {
    kotlin("jvm").version("2.0.21")
}

dependencies {
    api(kotlin("stdlib")) (1)
}
build.gradle
plugins {
    id("org.jetbrains.kotlin.jvm") version "2.0.21"
}

dependencies {
    api("org.jetbrains.kotlin:kotlin-stdlib:2.0.21") (1)
}
1 显式依赖 stdlib:此项目包含对 Kotlin 标准库的隐式依赖,这是编译其源代码所必需的。

而是这样做

build.gradle.kts
plugins {
    kotlin("jvm").version("2.0.21")
}
build.gradle
plugins {
    id("org.jetbrains.kotlin.jvm") version "2.0.21"
}

未显式包含 stdlib 依赖项:标准库仍然可用,需要它的源代码可以正常编译。

避免重复声明依赖项

避免多次声明同一个依赖项,特别是当它已通过传递性依赖或通过其他配置可用时。

解释

在 Gradle 构建脚本中重复依赖项可能导致

  • 维护成本增加:在多个地方声明依赖项会使其更难管理。

  • 意外行为:在多个配置(例如 compileOnlyimplementation)中声明同一个依赖项可能导致难以诊断的 classpath 问题。

示例

不要这样做

build.gradle.kts
plugins {
    `java-library`
}

dependencies {
    api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0") (1)
}
build.gradle
plugins {
    id 'java-library'
}

dependencies {
    api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0") (1)
}
1 implementation 作用域中重复的依赖项。

而是这样做

build.gradle.kts
plugins {
    `java-library`
}

dependencies {
    api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0") (1)
}
build.gradle
plugins {
    id 'java-library'
}

dependencies {
    api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0") (1)
}
1 只声明一次依赖项