在 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 標記決定

配置角色 可解析 可消費 可宣告

相依性作用域

false

false

true

解析以用於特定用途

true

false

false

公開給消費者

false

true

false

舊版,請勿使用

true

true

true

為了向後相容性,這些標記的預設值為 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 任務產生具有增強格式選項的 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 配置,以公開其他專案需要的執行時期相依性或成品。

    • 可消費的配置確保僅將必要的成品或相依性公開給消費者。

配置 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)
    }
}

可以使用相依性區塊在 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 必須管理對每個專案配置的存取。

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

  • 來自一個專案的任務直接在任務的動作中解析另一個專案中的配置。

  • 任務將來自另一個專案的配置指定為輸入檔案集合。

  • 一個專案的建置腳本在評估期間解析另一個專案中的配置。

  • 專案配置在設定檔中解析。

Gradle 會針對每個不安全的存取產生棄用警告。

不安全的存取可能會導致不確定的錯誤。您應修正建置中不安全的存取警告

在大多數情況下,您可以透過建立適當的跨專案相依性來解決不安全的存取。