在 Gradle 中,依赖项与特定的作用域相关联,例如编译时或运行时。这些作用域由配置表示,每个配置都由唯一的名称标识。

dependency management configurations

Gradle 插件通常会将预定义的 配置 添加到您的项目中。

例如,当应用 Java 插件时,会为您的项目添加用于源代码编译 (implementation)、测试执行 (testImplementation) 和更多 (apicompileOnlyruntimeOnly 等) 的配置。

build.gradle.kts
plugins {
    `java-library`
}
dependencies {
    implementation("org.hibernate:hibernate-core:3.6.7.Final")
    testImplementation("junit:junit:4.+")
    api("com.google.guava:guava:23.0")
}
build.gradle
plugins {
    id 'java-library'
}
dependencies {
    implementation 'org.hibernate:hibernate-core:3.6.7.Final'
    testImplementation 'junit:junit:4.+'
    api 'com.google.guava:guava:23.0'
}

此示例重点介绍了为 Java 项目在 implementationtestImplementationapi 配置上声明的依赖项。有关详细信息,请参阅 Java 插件文档

可解析和可消费的配置

配置不仅用于声明依赖项,它们还在依赖管理中扮演各种角色

  1. 声明依赖项角色:定义一组依赖项的配置。

  2. 消费者角色:用于将依赖项解析为工件的配置。

  3. 生产者角色:公开工件以供其他项目消费的配置。

1. 用于声明依赖项的配置(即,可声明配置)

要在您的项目中声明依赖项,您可以使用或创建可声明配置。这些配置有助于组织和分类项目中不同部分的依赖项。

例如,要表达对另一个项目的依赖关系,您可以使用像 implementation 这样的可声明配置

build.gradle.kts
dependencies {
    // add a project dependency to the implementation configuration
    implementation(project(":lib"))
}
build.gradle
dependencies {
    // add a project dependency to the implementation configuration
    implementation project(":lib")
}

用于声明依赖项的配置定义和管理您的代码执行编译或测试等任务所需的特定库或项目。

2. 用于消费者的配置(即,可解析配置)

要控制如何在您的项目中解析和使用依赖项,您可以使用或创建可解析配置。这些配置定义了您的项目在不同阶段(如编译或运行时)需要的类路径和其他工件集。

例如,implementation 配置声明依赖项,而 compileClasspathruntimeClasspath 是为特定目的设计的可解析配置。解析后,它们分别代表编译和运行时所需的类路径

build.gradle.kts
configurations {
    // declare a resolvable configuration that is going to resolve the compile classpath of the application
    resolvable("compileClasspath") {
        //isCanBeConsumed = false
        //isCanBeDeclared = false
        extendsFrom(implementation)
    }
}
build.gradle
configurations {
    // declare a resolvable configuration that is going to resolve the compile classpath of the application
    resolvable("compileClasspath") {
        //canBeConsumed = false
        //canBeDeclared = false
        extendsFrom(implementation)
    }
}

可解析配置是可以解析以生成一组文件或工件的配置。这些配置用于定义构建过程的不同阶段(例如编译或运行时)的类路径。

3. 用于生产者的配置(即,可消费配置)

可消费配置用于向其他项目公开工件。这些配置定义了您的项目的哪些部分可以被其他项目消费,例如 API 或运行时依赖项,但它们并非旨在在您的项目内直接解析。

例如,exposedApi 配置是一个可消费配置,它向消费者公开组件的 API

build.gradle.kts
configurations {
    // a consumable configuration meant for consumers that need the API of this component
    consumable("exposedApi") {
        //isCanBeResolved = false
        //isCanBeDeclared = false
        extendsFrom(implementation)
    }
}
build.gradle
configurations {
    // a consumable configuration meant for consumers that need the API of this component
    consumable("exposedApi") {
        //canBeResolved = false
        //canBeDeclared = false
        extendsFrom(implementation)
    }
}

一个库通常提供可消费配置,如 apiElements(用于编译)和 runtimeElements(用于运行时依赖项)。这些配置公开了其他项目消费所需的工件,而无需在当前项目内解析。canBeDeclaredisCanBeConsumedisCanBeResolved 标志有助于区分这些配置的角色。

配置标志和角色

配置有三个关键标志

  • canBeResolved: 表示此配置旨在将一组依赖项解析为依赖关系图。可解析配置不应是可声明的或可消费的。

  • canBeConsumed: 表示此配置旨在将工件公开到此项目之外。可消费配置不应是可声明的或可解析的。

  • canBeDeclared: 表示此配置旨在声明依赖项。可声明配置不应是可解析的或可消费的。

配置应仅启用这些标志之一。

简而言之,配置的角色由 canBeResolvedcanBeConsumedcanBeDeclared 标志确定

配置角色 可以解析 可以消费 可以声明

依赖作用域

为特定用途解析

公开给消费者

旧版,请勿使用

为了向后兼容,这些标志的默认值为 true,但作为插件作者,您应始终确定这些标志的正确值,否则您可能会意外引入解析错误。

此示例演示了如何在 Gradle 中手动声明核心 Java 配置(通常由 Java 插件 提供)

build.gradle.kts
// declare a "configuration" named "implementation"
val implementation by configurations.creating {
    isCanBeConsumed = false
    isCanBeResolved = false
}

dependencies {
    // add a project dependency to the implementation configuration
    implementation(project(":lib"))
}

configurations {
    // declare a resolvable configuration that is going to resolve the compile classpath of the application
    resolvable("compileClasspath") {
        //isCanBeConsumed = false
        //isCanBeDeclared = false
        extendsFrom(implementation)
    }
    // declare a resolvable configuration that is going to resolve the runtime classpath of the application
    resolvable("runtimeClasspath") {
        //isCanBeConsumed = false
        //isCanBeDeclared = false
        extendsFrom(implementation)
    }
}

configurations {
    // a consumable configuration meant for consumers that need the API of this component
    consumable("exposedApi") {
        //isCanBeResolved = false
        //isCanBeDeclared = false
        extendsFrom(implementation)
    }
    // a consumable configuration meant for consumers that need the implementation of this component
    consumable("exposedRuntime") {
        //isCanBeResolved = false
        //isCanBeDeclared = false
        extendsFrom(implementation)
    }
}
build.gradle
// declare a "configuration" named "implementation"
configurations {
    // declare a "configuration" named "implementation"
    implementation {
        canBeConsumed = false
        canBeResolved = false
    }
}

dependencies {
    // add a project dependency to the implementation configuration
    implementation project(":lib")
}

configurations {
    // declare a resolvable configuration that is going to resolve the compile classpath of the application
    resolvable("compileClasspath") {
        //canBeConsumed = false
        //canBeDeclared = false
        extendsFrom(implementation)
    }
    // declare a resolvable configuration that is going to resolve the runtime classpath of the application
    resolvable("runtimeClasspath") {
        //canBeConsumed = false
        //canBeDeclared = false
        extendsFrom(implementation)
    }
}

configurations {
    // a consumable configuration meant for consumers that need the API of this component
    consumable("exposedApi") {
        //canBeResolved = false
        //canBeDeclared = false
        extendsFrom(implementation)
    }
    // a consumable configuration meant for consumers that need the implementation of this component
    consumable("exposedRuntime") {
        //canBeResolved = false
        //canBeDeclared = false
        extendsFrom(implementation)
    }
}

创建了以下配置

  • implementation:用于声明项目依赖项,但既不被消费也不被解析。

  • compileClasspath + runtimeClasspath:可解析配置,用于从 implementation 收集编译时和运行时依赖项。

  • exposedApi + exposedRuntime:可消费配置,用于向其他项目公开工件(API 和运行时),但并非旨在用于内部解析。

此设置模仿了 Java 插件implementationcompileClasspathruntimeClasspathapiElementsruntimeElements 配置的行为。

已弃用的配置

过去,某些配置未定义它们打算用于哪个角色。

当以非预期的方式使用配置时,会发出弃用警告。要修复弃用,您需要停止以弃用的角色使用配置。所需的具体更改取决于配置的使用方式以及是否应使用替代配置。

创建自定义配置

您可以定义自定义配置,以为特定目的声明单独的依赖项作用域。

假设您想使用嵌入在 Java 源代码注释中的 AsciiDoc 格式生成 Javadoc。通过设置 asciidoclet 配置,您可以使 Gradle 能够使用 Asciidoclet,从而允许您的 Javadoc task 生成具有增强格式选项的 HTML 文档

build.gradle.kts
val asciidoclet by configurations.creating

dependencies {
    asciidoclet("org.asciidoctor:asciidoclet:1.+")
}

tasks.register("configureJavadoc") {
    doLast {
        tasks.javadoc {
            options.doclet = "org.asciidoctor.Asciidoclet"
            options.docletpath = asciidoclet.files.toList()
        }
    }
}
build.gradle
configurations {
    asciidoclet
}

dependencies {
    asciidoclet 'org.asciidoctor:asciidoclet:1.+'
}

您可以使用 configurations 块管理自定义配置。配置必须具有名称,并且可以相互扩展。有关更多详细信息,请参阅 ConfigurationContainer API。

配置旨在用于单一角色:声明依赖项、执行解析或定义可消费的变体。

创建自定义配置主要有三种用例

  1. API/实现分离: 创建自定义配置以将 API 依赖项(公开给消费者)与实现依赖项(在编译或运行时在内部使用)分开。

    • 您可以为消费者将依赖的库创建 api 配置,并为仅在内部需要的库创建 implementation 配置。api 配置通常被下游项目消费,而 implementation 依赖项对消费者隐藏,但在内部使用。

    • 这种分离确保您的项目在其公共 API 和严格的内部机制之间保持清晰的界限。

  2. 可解析配置创建:创建自定义可解析配置以解析特定组的依赖项,例如类路径,在不同的构建阶段。

    • 您可以创建一个 compileClasspath 配置,该配置仅解析编译项目所需的依赖项。同样,您可以创建一个 runtimeClasspath 配置,以解析运行时运行项目所需的依赖项。

    • 这允许对在不同构建阶段(例如编译或测试)可用的依赖项进行细粒度控制。

  3. 来自依赖配置的可消费配置:创建自定义可消费配置以公开工件或依赖项供其他项目消费,通常在您的项目生成 JAR 等工件时。

    • 您可以创建一个 exposedApi 配置,以公开您的项目的 API 依赖项,供其他项目消费。同样,可以创建一个 runtimeElements 配置,以公开其他项目需要的运行时依赖项或工件。

    • 可消费配置确保仅向消费者公开必要的工件或依赖项。

Configuration API 孵化方法

ConfigurationContainer API 中,有几个孵化工厂方法——resolvable()consumable()dependencyScope()——可用于简化创建具有特定角色的配置。

这些方法帮助构建作者记录配置的用途,并避免手动设置各种配置标志,从而简化流程并确保一致性

  • resolvable():创建一个旨在解析依赖项的配置。这意味着该配置可用于解析依赖项,但不能被其他项目消费。

  • consumable():创建一个旨在被其他项目消费但本身不用于解析依赖项的配置。

  • dependencyScope():创建一个建立依赖项作用域的配置,设置必要的属性以充当消费者和提供者,具体取决于用例。

配置继承

配置可以从其他配置继承,从而创建继承层次结构。

配置使用 Configuration.extendsFrom(Configuration…​) 方法形成继承层次结构。配置可以扩展任何其他配置,除了 detached configuration,无论它在构建脚本或插件中是如何定义的。

避免使用非可消费或可解析的配置扩展可消费或可解析的配置。

配置只能扩展同一项目内的配置。

扩展配置时,新配置会继承

  • 依赖项

  • 依赖约束

  • 排除规则

  • 工件

  • 能力

扩展包括属性。它也扩展可消费/可解析/可声明状态。

依赖解析

所有依赖解析 API 的入口点是可解析Configuration。Java 插件主要使用 compileClasspathruntimeClasspath 配置来分别解析编译和运行时的 jar 包。

可解析配置旨在启动依赖解析。要解析的依赖项在依赖作用域配置上声明。Java 插件使用 apiimplementationruntimeOnly 依赖作用域配置等作为可解析配置要解析的依赖项的来源。

考虑以下示例,该示例演示了如何声明一组旨在用于解析的配置

此示例使用孵化 API。
build.gradle.kts
val implementation = configurations.dependencyScope("implementation")
val runtimeClasspath = configurations.resolvable("runtimeClasspath") {
    extendsFrom(implementation.get())
}
build.gradle
configurations {
    dependencyScope("implementation")
    resolvable("runtimeClasspath") {
        extendsFrom(implementation)
    }
}

可以使用 dependencies 块在 implementation 配置上声明依赖项。有关可以声明的依赖项类型以及自定义依赖项声明的各种选项的更多信息,请参阅 声明依赖项 章节。

build.gradle.kts
dependencies {
    implementation("com.google.guava:guava:33.2.1-jre")
}
build.gradle
dependencies {
    implementation("com.google.guava:guava:33.2.1-jre")
}

现在我们已经创建了一个用于声明依赖项的依赖作用域配置,以及一个用于解析这些依赖项的可解析配置,我们可以使用 Gradle 的 依赖解析 API 来访问解析结果。

不安全的配置解析错误

解析配置可能会对 Gradle 的项目模型产生副作用。因此,Gradle 必须管理对每个项目配置的访问。

配置可能以不安全方式解析的方式有很多。例如

  • 一个项目中的 task 直接在 task 的 action 中解析另一个项目中的配置。

  • 一个 task 将来自另一个项目的配置指定为输入文件集合。

  • 一个项目的构建脚本在评估期间解析另一个项目中的配置。

  • 项目配置在 settings 文件中解析。

Gradle 为每个不安全访问生成弃用警告。

不安全访问可能会导致不确定的错误。您应该 修复不安全访问警告 在您的构建中。

在大多数情况下,您可以通过创建 正确的跨项目依赖 来解决不安全访问问题。