依赖项的集中声明

使用版本目录

版本目录是依赖项列表,表示为依赖项坐标,用户在构建脚本中声明依赖项时可以选择该列表。

例如,可以使用字符串表示法声明依赖项,也可以从版本目录中选择依赖项坐标

build.gradle.kts
dependencies {
    implementation(libs.groovy.core)
}
build.gradle
dependencies {
    implementation(libs.groovy.core)
}

在此上下文中,libs 是一个目录,groovy 表示该目录中可用的依赖项。版本目录相较于在构建脚本中直接声明依赖项提供了许多优势

  • 对于每个目录,Gradle 都会生成类型安全访问器,以便您可以在 IDE 中使用自动完成功能轻松添加依赖项。

  • 每个目录对构建的所有项目都是可见的。它是声明依赖项版本并确保对该版本的更改应用于每个子项目的中心位置。

  • 目录可以声明依赖项包,即通常一起使用的“依赖项组”。

  • 目录可以将依赖项的组和名称与其实际版本分开,并使用版本引用,从而可以在多个依赖项之间共享版本声明。

使用 libs.someLib 符号添加依赖项的工作方式与直接在构建脚本中硬编码组、工件和版本完全相同。

依赖项目录不会强制执行依赖项的版本:与常规依赖项符号一样,它声明请求的版本或丰富版本。在冲突解决期间,该版本不一定是被选中的版本。

声明版本目录

可以在 settings.gradle(.kts) 文件中声明版本目录。在上面的示例中,为了通过 libs 目录提供 groovy,我们需要将别名与 GAV(组、工件、版本)坐标相关联

settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            library("groovy-core", "org.codehaus.groovy:groovy:3.0.5")
            library("groovy-json", "org.codehaus.groovy:groovy-json:3.0.5")
            library("groovy-nio", "org.codehaus.groovy:groovy-nio:3.0.5")
            library("commons-lang3", "org.apache.commons", "commons-lang3").version {
                strictly("[3.8, 4.0[")
                prefer("3.9")
            }
        }
    }
}
settings.gradle
dependencyResolutionManagement {
    versionCatalogs {
        libs {
            library('groovy-core', 'org.codehaus.groovy:groovy:3.0.5')
            library('groovy-json', 'org.codehaus.groovy:groovy-json:3.0.5')
            library('groovy-nio', 'org.codehaus.groovy:groovy-nio:3.0.5')
            library('commons-lang3', 'org.apache.commons', 'commons-lang3').version {
                strictly '[3.8, 4.0['
                prefer '3.9'
            }
        }
    }
}

别名及其到类型安全访问器的映射

别名必须由一系列标识符组成,这些标识符由破折号 (-,推荐)、下划线 (_) 或点 (.) 分隔。标识符本身必须由 ASCII 字符组成,最好是小写,最后可以跟数字。

例如

  • guava 是一个有效的别名

  • groovy-core 是一个有效的别名

  • commons-lang3 是一个有效的别名

  • androidx.awesome.lib 也是一个有效的别名

  • this.#is.not!

然后,为每个子组生成类型安全访问器。例如,给定名为 libs 的版本目录中的以下别名

guavagroovy-coregroovy-xmlgroovy-jsonandroidx.awesome.lib

我们将生成以下类型安全访问器

  • libs.guava

  • libs.groovy.core

  • libs.groovy.xml

  • libs.groovy.json

  • libs.androidx.awesome.lib

其中 libs 前缀来自版本目录名称。

如果您想避免生成子组访问器,我们建议依靠大小写进行区分。例如,别名 groovyCoregroovyJsongroovyXml 将分别映射到 libs.groovyCorelibs.groovyJsonlibs.groovyXml 访问器。

在声明别名时,值得注意的是,任何 -_. 字符都可以用作分隔符,但生成的目录将全部标准化为 .:例如,foo-bar 作为别名会自动转换为 foo.bar

一些关键字是保留的,因此不能用作别名。以下单词不能用作别名

  • extensions

  • class

  • convention

此外,以下单词不能用作依赖项的别名的第一个子组(对于捆绑包、版本和插件,此限制不适用)

  • bundles

  • versions

  • plugins

因此,例如,对于依赖项,别名 versions-dependency 无效,但 versionsDependencydependency-versions 有效。

具有相同版本号的依赖项

声明版本目录 中的第一个示例中,我们可以看到我们为 groovy 库的各个组件声明了 3 个别名,并且它们都共享相同的版本号。

我们可以声明一个版本并引用它,而不是重复相同的版本号

settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            version("groovy", "3.0.5")
            version("checkstyle", "8.37")
            library("groovy-core", "org.codehaus.groovy", "groovy").versionRef("groovy")
            library("groovy-json", "org.codehaus.groovy", "groovy-json").versionRef("groovy")
            library("groovy-nio", "org.codehaus.groovy", "groovy-nio").versionRef("groovy")
            library("commons-lang3", "org.apache.commons", "commons-lang3").version {
                strictly("[3.8, 4.0[")
                prefer("3.9")
            }
        }
    }
}
settings.gradle
dependencyResolutionManagement {
    versionCatalogs {
        libs {
            version('groovy', '3.0.5')
            version('checkstyle', '8.37')
            library('groovy-core', 'org.codehaus.groovy', 'groovy').versionRef('groovy')
            library('groovy-json', 'org.codehaus.groovy', 'groovy-json').versionRef('groovy')
            library('groovy-nio', 'org.codehaus.groovy', 'groovy-nio').versionRef('groovy')
            library('commons-lang3', 'org.apache.commons', 'commons-lang3').version {
                strictly '[3.8, 4.0['
                prefer '3.9'
            }
        }
    }
}

单独声明的版本也可以通过类型安全访问器获得,这使得它们可用于比依赖项版本更多的用例,特别是对于工具

build.gradle.kts
checkstyle {
    // will use the version declared in the catalog
    toolVersion = libs.versions.checkstyle.get()
}
build.gradle
checkstyle {
    // will use the version declared in the catalog
    toolVersion = libs.versions.checkstyle.get()
}

如果声明的版本别名也是某些更具体别名的前缀,如 libs.versions.zinclibs.versions.zinc.apiinfo,则可以通过类型安全访问器的 asProvider() 使用更通用版本的价值

build.gradle.kts
scala {
    zincVersion = libs.versions.zinc.asProvider().get()
}
build.gradle
scala {
    zincVersion = libs.versions.zinc.asProvider().get()
}

在目录中声明的依赖项通过与它们名称相对应的扩展公开给构建脚本。在上面的示例中,因为在设置中声明的目录名为 libs,所以扩展可以通过当前构建的所有构建脚本中的名称 libs 使用。使用以下符号声明依赖项…​

build.gradle.kts
dependencies {
    implementation(libs.groovy.core)
    implementation(libs.groovy.json)
    implementation(libs.groovy.nio)
}
build.gradle
dependencies {
    implementation libs.groovy.core
    implementation libs.groovy.json
    implementation libs.groovy.nio
}

…​与编写 完全相同

build.gradle.kts
dependencies {
    implementation("org.codehaus.groovy:groovy:3.0.5")
    implementation("org.codehaus.groovy:groovy-json:3.0.5")
    implementation("org.codehaus.groovy:groovy-nio:3.0.5")
}
build.gradle
dependencies {
    implementation 'org.codehaus.groovy:groovy:3.0.5'
    implementation 'org.codehaus.groovy:groovy-json:3.0.5'
    implementation 'org.codehaus.groovy:groovy-nio:3.0.5'
}

在目录中声明的版本是 丰富版本。有关完整版本声明支持文档,请参阅 版本目录构建器 API

依赖项包

因为在不同的项目中经常系统地一起使用一些依赖项,所以版本目录提供了“依赖项包”的概念。一个包基本上是几个依赖项的别名。例如,您可以编写以下内容,而不是像上面那样声明 3 个单独的依赖项

build.gradle.kts
dependencies {
    implementation(libs.bundles.groovy)
}
build.gradle
dependencies {
    implementation libs.bundles.groovy
}

名为 groovy 的包需要在目录中声明

settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            version("groovy", "3.0.5")
            version("checkstyle", "8.37")
            library("groovy-core", "org.codehaus.groovy", "groovy").versionRef("groovy")
            library("groovy-json", "org.codehaus.groovy", "groovy-json").versionRef("groovy")
            library("groovy-nio", "org.codehaus.groovy", "groovy-nio").versionRef("groovy")
            library("commons-lang3", "org.apache.commons", "commons-lang3").version {
                strictly("[3.8, 4.0[")
                prefer("3.9")
            }
            bundle("groovy", listOf("groovy-core", "groovy-json", "groovy-nio"))
        }
    }
}
settings.gradle
dependencyResolutionManagement {
    versionCatalogs {
        libs {
            version('groovy', '3.0.5')
            version('checkstyle', '8.37')
            library('groovy-core', 'org.codehaus.groovy', 'groovy').versionRef('groovy')
            library('groovy-json', 'org.codehaus.groovy', 'groovy-json').versionRef('groovy')
            library('groovy-nio', 'org.codehaus.groovy', 'groovy-nio').versionRef('groovy')
            library('commons-lang3', 'org.apache.commons', 'commons-lang3').version {
                strictly '[3.8, 4.0['
                prefer '3.9'
            }
            bundle('groovy', ['groovy-core', 'groovy-json', 'groovy-nio'])
        }
    }
}

语义再次等效:添加单个包等效于单独添加构成包的所有依赖项。

插件

除了库之外,版本目录还支持声明插件版本。虽然库由其组、工件和版本坐标表示,但 Gradle 插件仅由其 ID 和版本标识。因此,它们需要单独声明

您不能在设置文件或设置插件中使用在版本目录中声明的插件(因为目录本身在设置中定义,所以这将是一个先有鸡还是先有蛋的问题)。
settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            plugin("versions", "com.github.ben-manes.versions").version("0.45.0")
        }
    }
}
settings.gradle
dependencyResolutionManagement {
    versionCatalogs {
        libs {
            plugin('versions', 'com.github.ben-manes.versions').version('0.45.0')
        }
    }
}

然后,插件在 plugins 块中可访问,并且可以使用以下方法在构建的任何项目中使用

build.gradle.kts
plugins {
    `java-library`
    checkstyle
    alias(libs.plugins.versions)
}
build.gradle
plugins {
    id 'java-library'
    id 'checkstyle'
    // Use the plugin `versions` as declared in the `libs` version catalog
    alias(libs.plugins.versions)
}

使用多个目录

除了传统的 libs 目录之外,还可以通过 Settings API 声明任意数量的目录。这允许您以对项目有意义的方式在多个源中分离依赖项声明。

settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        create("testLibs") {
            val junit5 = version("junit5", "5.7.1")
            library("junit-api", "org.junit.jupiter", "junit-jupiter-api").versionRef(junit5)
            library("junit-engine", "org.junit.jupiter", "junit-jupiter-engine").versionRef(junit5)
        }
    }
}
settings.gradle
dependencyResolutionManagement {
    versionCatalogs {
        testLibs {
            def junit5 = version('junit5', '5.7.1')
            library('junit-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef(junit5)
            library('junit-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef(junit5)
        }
    }
}

每个目录都会生成一个扩展,应用于所有项目以访问其内容。因此,通过选择一个减少潜在冲突的名称来降低冲突的可能性是有意义的。例如,一种选择是选择一个以 Libs 结尾的名称。

libs.versions.toml 文件

除了上述设置 API 之外,Gradle 还提供了一个传统文件来声明目录。如果在根构建的 gradle 子目录中找到 libs.versions.toml 文件,那么将使用此文件的目录自动声明。

声明 libs.versions.toml 文件并不会使其成为依赖项的唯一真实来源:它是一个可以声明依赖项的传统位置。一旦开始使用目录,强烈建议在目录中声明所有依赖项,而不是在构建脚本中硬编码组/工件/版本字符串。请注意,插件可能会添加依赖项,这些依赖项是在此文件外部定义的。

就像 src/main/java 是查找 Java 源的约定,这并不会阻止声明其他源目录(在构建脚本或插件中),libs.versions.toml 文件的存在也不会阻止在其他地方声明依赖项。

但是,此文件的存在确实表明大多数(如果不是全部)依赖项都将在该文件中声明。因此,对于大多数用户来说,更新依赖项版本只需更改此文件中的一个行。

默认情况下,libs.versions.toml 文件将成为 libs 目录的输入。可以更改默认目录的名称,例如,如果您已经具有同名的扩展

settings.gradle.kts
dependencyResolutionManagement {
    defaultLibrariesExtensionName = "projectLibs"
}
settings.gradle
dependencyResolutionManagement {
    defaultLibrariesExtensionName = 'projectLibs'
}

版本目录 TOML 文件格式

TOML 文件包含 4 个主要部分

  • [versions] 部分用于声明依赖项可以引用的版本

  • [libraries] 部分用于声明别名以进行坐标

  • [bundles] 部分用于声明依赖项包

  • [plugins] 部分用于声明插件

例如

libs.versions.toml 文件
[versions]
groovy = "3.0.5"
checkstyle = "8.37"

[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" } }

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

[plugins]
versions = { id = "com.github.ben-manes.versions", version = "0.45.0" }

版本可以声明为单个字符串,在这种情况下,它们将被解释为必需版本,或作为 丰富版本

[versions]
my-lib = { strictly = "[1.0, 2.0[", prefer = "1.2" }

版本声明支持的成员为

依赖项声明可以声明为简单字符串,在这种情况下,它们将被解释为 group:artifact:version 坐标,或将版本声明与组和名称分开

对于别名,别名及其到类型安全访问器的映射 部分中描述的规则也适用。
不同的依赖项表示法
[versions]
common = "1.4"

[libraries]
my-lib = "com.mycompany:mylib:1.4"
my-other-lib = { module = "com.mycompany:other", version = "1.4" }
my-other-lib2 = { group = "com.mycompany", name = "alternate", version = "1.4" }
mylib-full-format = { group = "com.mycompany", name = "alternate", version = { require = "1.4" } }

[plugins]
short-notation = "some.plugin.id:1.4"
long-notation = { id = "some.plugin.id", version = "1.4" }
reference-notation = { id = "some.plugin.id", version.ref = "common" }

如果您想要引用在 [versions] 部分中声明的版本,则应使用 version.ref 属性

[versions]
some = "1.4"

[libraries]
my-lib = { group = "com.mycompany", name="mylib", version.ref="some" }

TOML 文件格式非常宽松,允许您编写“点”属性作为完整对象声明的快捷方式。例如,以下内容

a.b.c="d"

等效于

a.b = { c = "d" }

a = { b = { c = "d" } }

有关详细信息,请参阅 TOML 规范

类型不安全的 API

可以通过类型不安全的 API 访问版本目录。在没有生成访问器的情况下,可以使用此 API。它通过版本目录扩展进行访问

build.gradle.kts
val versionCatalog = extensions.getByType<VersionCatalogsExtension>().named("libs")
println("Library aliases: ${versionCatalog.libraryAliases}")
dependencies {
    versionCatalog.findLibrary("groovy-json").ifPresent {
        implementation(it)
    }
}
build.gradle
def versionCatalog = extensions.getByType(VersionCatalogsExtension).named("libs")
println "Library aliases: ${versionCatalog.libraryAliases}"
dependencies {
    versionCatalog.findLibrary("groovy-json").ifPresent {
        implementation(it)
    }
}

查看 版本目录 API 以了解所有受支持的方法。

共享目录

版本目录用于单个构建(可能是多项目构建),但也可以在构建之间共享。例如,组织可能希望创建不同团队的不同项目可能使用的依赖项目录。

从 TOML 文件导入目录

版本目录构建器 API 支持包含来自外部文件中的模型。这使得在需要时,可以为 buildSrc 重用主构建的目录。例如,buildSrc/settings.gradle(.kts) 文件可以使用以下内容包含此文件

settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            from(files("../gradle/libs.versions.toml"))
        }
    }
}
settings.gradle
dependencyResolutionManagement {
    versionCatalogs {
        libs {
            from(files("../gradle/libs.versions.toml"))
        }
    }
}

使用 VersionCatalogBuilder.from(Object dependencyNotation) 方法时,将只接受一个文件。这意味着像 Project.files(java.lang.Object…​) 这样的符号必须引用一个文件,否则构建将失败。

如果需要更复杂结构(从多个文件导入版本目录),建议使用基于代码的方法,而不是 TOML 文件。

因此,此技术可用于从不同文件声明多个目录

settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        // declares an additional catalog, named 'testLibs', from the 'test-libs.versions.toml' file
        create("testLibs") {
            from(files("gradle/test-libs.versions.toml"))
        }
    }
}
settings.gradle
dependencyResolutionManagement {
    versionCatalogs {
        // declares an additional catalog, named 'testLibs', from the 'test-libs.versions.toml' file
        testLibs {
            from(files('gradle/test-libs.versions.toml'))
        }
    }
}

版本目录插件

虽然从本地文件导入目录很方便,但它并不能解决在组织中或外部使用者之间共享目录的问题。共享目录的一种选择是编写一个设置插件,在 Gradle 插件门户网站或内部存储库上发布它,并让使用者在其设置文件中应用该插件。

或者,Gradle 提供了一个版本目录插件,它提供了声明然后发布目录的功能。

为此,您需要应用 version-catalog 插件

build.gradle.kts
plugins {
    `version-catalog`
    `maven-publish`
}
build.gradle
plugins {
    id 'version-catalog'
    id 'maven-publish'
}

然后,此插件将公开 catalog 扩展,您可以使用它来声明目录

示例 17. 目录定义
build.gradle.kts
catalog {
    // declare the aliases, bundles and versions in this block
    versionCatalog {
        library("my-lib", "com.mycompany:mylib:1.2")
    }
}
build.gradle
catalog {
    // declare the aliases, bundles and versions in this block
    versionCatalog {
        library('my-lib', 'com.mycompany:mylib:1.2')
    }
}

然后,可以通过应用 maven-publishivy-publish 插件并配置发布以使用 versionCatalog 组件来发布此类目录

示例 18. 发布目录
build.gradle.kts
publishing {
    publications {
        create<MavenPublication>("maven") {
            from(components["versionCatalog"])
        }
    }
}
build.gradle
publishing {
    publications {
        maven(MavenPublication) {
            from components.versionCatalog
        }
    }
}

发布此类项目时,将自动生成(并上传)一个 libs.versions.toml 文件,然后可以 从其他 Gradle 构建中使用

导入已发布的目录

可以通过设置 API 导入 版本目录插件 生成的目录

settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            from("com.mycompany:catalog:1.0")
        }
    }
}
settings.gradle
dependencyResolutionManagement {
    versionCatalogs {
        libs {
            from("com.mycompany:catalog:1.0")
        }
    }
}

覆盖目录版本

如果目录声明了一个版本,则可以在导入目录时覆盖该版本

settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        create("amendedLibs") {
            from("com.mycompany:catalog:1.0")
            // overwrite the "groovy" version declared in the imported catalog
            version("groovy", "3.0.6")
        }
    }
}
settings.gradle
dependencyResolutionManagement {
    versionCatalogs {
        amendedLibs {
            from("com.mycompany:catalog:1.0")
            // overwrite the "groovy" version declared in the imported catalog
            version("groovy", "3.0.6")
        }
    }
}

在上面的示例中,任何使用 groovy 版本作为引用的依赖项都将自动更新为使用 3.0.6

同样,覆盖版本并不意味着实际已解析的依赖项版本将相同:这只会更改导入的内容,也就是说,在声明依赖项时使用的内容。实际版本将受到传统冲突解决(如果有)的影响。

使用平台控制传递版本

一个 平台是一个特殊的软件组件,可用于控制传递依赖项版本。在大多数情况下,它完全由 依赖项约束 组成,这些约束将建议依赖项版本或强制某些版本。因此,无论何时需要在项目之间共享依赖项版本,这都是一个完美的工具。在这种情况下,项目通常会以这种方式组织

  • 一个 platform 项目,该项目为不同子项目中发现的各种依赖项定义约束

  • 许多依赖于平台的子项目和声明无版本的依赖项

在 Java 生态系统中,Gradle 为此提供了一个 插件

还可以找到作为 Maven BOM 发布的平台,Gradle 原生支持

使用 platform 关键字创建对平台的依赖项

build.gradle.kts
dependencies {
    // get recommended versions from the platform project
    api(platform(project(":platform")))
    // no version required
    api("commons-httpclient:commons-httpclient")
}
build.gradle
dependencies {
    // get recommended versions from the platform project
    api platform(project(':platform'))
    // no version required
    api 'commons-httpclient:commons-httpclient'
}

platform 符号是一种简写符号,它实际上在后台执行多个操作

  • 它将 org.gradle.category 属性 设置为 platform,这意味着 Gradle 将选择依赖项的platform 组件。

  • 它默认设置 endorseStrictVersions 行为,这意味着如果平台声明了严格依赖项,则将强制执行这些依赖项。

这意味着默认情况下,对平台的依赖项会触发继承该平台中定义的所有 严格版本,这对于平台作者确保所有使用者在依赖项版本方面尊重其决策非常有用。可以通过显式调用 doNotEndorseStrictVersions 方法来关闭此功能。

导入 Maven BOM

Gradle 提供了导入 物料清单 (BOM) 文件 的支持,这些文件实际上是使用 <dependencyManagement> 来控制直接和传递依赖项的依赖项版本的 .pom 文件。Gradle 中的 BOM 支持类似于在 Maven 中依赖 BOM 时使用 <scope>import</scope>。然而,在 Gradle 中,它是通过对 BOM 的常规依赖项声明完成的

build.gradle.kts
dependencies {
    // import a BOM
    implementation(platform("org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE"))

    // define dependencies without versions
    implementation("com.google.code.gson:gson")
    implementation("dom4j:dom4j")
}
build.gradle
dependencies {
    // import a BOM
    implementation platform('org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE')

    // define dependencies without versions
    implementation 'com.google.code.gson:gson'
    implementation 'dom4j:dom4j'
}

在示例中,gsondom4j 的版本由 Spring Boot BOM 提供。这样,如果你正在为 Spring Boot 等平台开发,则不必自己声明任何版本,而是可以依赖平台提供的版本。

Gradle 将 BOM 的 <dependencyManagement> 块中的所有条目视为类似于 Gradle 的依赖项约束。这意味着 <dependencyManagement> 块中定义的任何版本都可能影响依赖项解析结果。为了符合 BOM 的资格,.pom 文件需要设置 <packaging>pom</packaging>

然而,BOM 通常不仅提供版本作为建议,还提供一种覆盖图中找到的任何其他版本的方法。在导入 BOM 时,可以通过使用 enforcedPlatform 关键字(而不是 platform)来启用此行为

build.gradle.kts
dependencies {
    // import a BOM. The versions used in this file will override any other version found in the graph
    implementation(enforcedPlatform("org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE"))

    // define dependencies without versions
    implementation("com.google.code.gson:gson")
    implementation("dom4j:dom4j")

    // this version will be overridden by the one found in the BOM
    implementation("org.codehaus.groovy:groovy:1.8.6")
}
build.gradle
dependencies {
    // import a BOM. The versions used in this file will override any other version found in the graph
    implementation enforcedPlatform('org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE')

    // define dependencies without versions
    implementation 'com.google.code.gson:gson'
    implementation 'dom4j:dom4j'

    // this version will be overridden by the one found in the BOM
    implementation 'org.codehaus.groovy:groovy:1.8.6'
}

如果你的软件组件可以被其他人使用,则需要谨慎考虑使用 enforcedPlatform。此声明实际上是传递性的,因此将应用于使用者的依赖项图。不幸的是,如果他们碰巧不同意其中一个强制版本,他们将不得不使用 exclude。相反,如果你的可重用软件组件对某些第三方依赖项版本有强烈意见,请考虑使用带有 strictly富版本声明

我应该使用平台还是目录?

由于平台和目录都涉及依赖项版本,并且都可以用于在项目中共享依赖项版本,因此可能会混淆使用哪一个以及是否一个优于另一个。

简而言之,您应该

  • 仅将目录用于定义项目依赖项及其版本并生成类型安全访问器

  • 使用平台将版本应用于依赖项图并影响依赖项解析

目录有助于集中依赖项版本,并且正如其名称所暗示的那样,它只是您可以从中选择的依赖项目录。我们建议在所有情况下都使用它来声明依赖项的坐标。Gradle 将使用它来生成类型安全访问器,为外部依赖项提供简短符号,并且它允许轻松地在不同项目之间共享这些坐标。使用目录不会对下游使用者产生任何影响:对他们来说是透明的。

平台是一个更重量级的构造:它是依赖项图的组件,就像任何其他库一样。如果您依赖于平台,则该平台本身就是图中的一个组件。具体而言,这意味着

  • 约束在平台中定义的约束可以影响传递依赖项,而不仅仅是您项目的直接依赖项。

  • 平台是版本化的,并且图中的传递依赖项可以依赖于不同版本的平台,从而导致各种依赖项升级。

  • 平台可以将组件捆绑在一起,特别是可以作为对齐版本的构造。

  • 对平台的依赖关系是由您的依赖项使用者“继承”的:这意味着对平台的依赖关系可能会影响您的使用者将使用的库版本,即使您不直接或传递依赖于平台引用的组件。

总之,使用目录始终是一种良好的工程实践,因为它集中了通用定义,允许共享依赖项版本或插件版本,但它是构建的“实现细节”:它对使用者不可见,并且目录中未使用的元素只是被忽略。

平台旨在影响依赖项解析图,例如通过对传递依赖项添加约束:它是一种用于构建依赖项图并影响解析结果的解决方案。

在实践中,您的项目既可以使用目录,还可以声明一个平台,该平台本身使用该目录

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

dependencies {
    constraints {
        api(libs.mylib)
    }
}
build.gradle
plugins {
    id 'java-platform'
}

dependencies {
    constraints {
        api(libs.mylib)
    }
}