Gradle 支援功能的概念:通常情況下,單一程式庫可以分割成多個相關但不同的程式庫,其中每個功能可以用在主程式庫旁邊。

功能允許元件公開多個相關程式庫,每個程式庫都可以宣告自己的相依性。這些程式庫公開為變異,類似於主程式庫公開其 API 和執行時期的變異方式。

這允許許多不同的情況(清單不完整)

透過功能選擇功能

宣告依賴於某個元件通常是透過提供一組座標 (群組、成品、版本,也稱為 GAV 座標) 來完成的。這讓引擎得以判斷我們正在尋找的元件,但此類元件可能會提供不同的變異變異通常會根據使用方式來選擇。例如,我們可能會選擇不同的變異來編譯元件 (在這種情況下,我們需要元件的 API) 或執行程式碼 (在這種情況下,我們需要元件的執行時間)。元件的所有變異都會提供許多功能,這些功能會使用 GAV 座標以類似的方式表示。

功能會以 GAV 座標表示,但您必須將其視為功能說明

  • "我提供 SLF4J 繫結"

  • "我提供 MySQL 的執行時間支援"

  • "我提供 Groovy 執行時間"

而且一般來說,在圖形中擁有兩個提供相同東西的元件會造成問題 (它們會衝突)。

這是個重要的概念,因為

  • 預設情況下,變異會提供與其元件的 GAV 座標對應的功能

  • 依賴圖形中沒有兩個變異可以提供相同的功能

  • 只要提供不同的功能,就可以選取單一元件的許多變異

典型的元件只會提供具有預設功能的變異。例如,Java 函式庫會公開兩個變異 (API 和執行時間),它們提供相同的功能。因此,在依賴圖形中同時擁有單一元件的API執行時間會造成錯誤。

不過,假設您需要元件的執行時間測試固定裝置執行時間。只要函式庫的執行時間測試固定裝置執行時間變異宣告不同的功能,這樣就是允許的。

如果我們這樣做,消費者就必須宣告兩個依賴關係

  • 一個在「主要」功能上,也就是函式庫

  • 一個在「測試固定裝置」功能上,透過要求其功能

雖然解析引擎獨立於生態系統支援多變異元件,但目前只能使用 Java 外掛程式來使用功能

註冊功能

透過套用 java-library 外掛,可以宣告功能。下列程式碼說明如何宣告名為 mongodbSupport 的功能

範例 1. 註冊功能
build.gradle.kts
sourceSets {
    create("mongodbSupport") {
        java {
            srcDir("src/mongodb/java")
        }
    }
}

java {
    registerFeature("mongodbSupport") {
        usingSourceSet(sourceSets["mongodbSupport"])
    }
}
build.gradle
sourceSets {
    mongodbSupport {
        java {
            srcDir 'src/mongodb/java'
        }
    }
}

java {
    registerFeature('mongodbSupport') {
        usingSourceSet(sourceSets.mongodbSupport)
    }
}

Gradle 會自動為您設定許多事項,其方式與 Java 函式庫外掛 設定組態的方式非常類似。

相依性範圍組態的建立方式與主功能相同

  • 組態 mongodbSupportApi,用於宣告此功能的 API 相依性

  • 組態 mongodbSupportImplementation,用於宣告此功能的實作相依性

  • 組態 mongodbSupportRuntimeOnly,用於宣告此功能的執行時期相依性

  • 組態 mongodbSupportCompileOnly,用於宣告此功能的編譯時期相依性

  • 組態 mongodbSupportCompileOnlyApi,用於宣告此功能的編譯時期 API 相依性

此外,可消耗組態的建立方式與主功能相同

  • 組態 mongodbSupportApiElements,供消費者用來擷取此功能的成品和 API 相依性

  • 組態 mongodbSupportRuntimeElements,供消費者用來擷取此功能的成品和執行時期相依性

功能應具有同名的來源組。Gradle 會建立一個 Jar 工作,以將從功能來源組建置的類別打包,並使用與功能的 kebab-case 名稱對應的分類器。

註冊功能時,請勿使用來源組。此行為將在 Gradle 的未來版本中被棄用。

大多數使用者只需要關注相依性範圍組態,即可宣告此功能的特定相依性

build.gradle.kts
dependencies {
    "mongodbSupportImplementation"("org.mongodb:mongodb-driver-sync:3.9.1")
}
build.gradle
dependencies {
    mongodbSupportImplementation 'org.mongodb:mongodb-driver-sync:3.9.1'
}

依慣例,Gradle 會將功能名稱對應到功能,其群組和版本分別與主元件的群組和版本相同,但其名稱是主元件名稱加上 -,後接 kebab-case 功能名稱。

例如,如果元件的群組是 org.gradle.demo,其名稱是 provider,其版本是 1.0,且功能名稱是 mongodbSupport,則功能的變體將具有 org.gradle.demo:provider-mongodb-support:1.0 功能。

如果您自行選擇功能名稱或為變體新增更多功能,建議遵循相同的慣例。

發佈功能

根據元資料檔案格式,發佈功能可能會造成資料遺失

  • 使用 Gradle 模組元資料,所有內容都會發佈,消費者將能充分享受功能

  • 使用 POM 元資料 (Maven),功能會以選用相依性發佈,而功能的成品會以不同的分類器發佈

  • 使用 Ivy 元資料,功能會以額外組態發佈,這些組態不會default組態延伸

僅支援使用 maven-publishivy-publish 外掛發佈功能。Java 函式庫外掛會負責為您註冊額外的變體,因此不需要額外的組態,只需要一般的發佈

build.gradle.kts
plugins {
    `java-library`
    `maven-publish`
}
// ...
publishing {
    publications {
        create("myLibrary", MavenPublication::class.java) {
            from(components["java"])
        }
    }
}
build.gradle
plugins {
    id 'java-library'
    id 'maven-publish'
}
// ...
publishing {
    publications {
        myLibrary(MavenPublication) {
            from components.java
        }
    }
}

新增 javadoc 和來源 JAR

類似於 主要的 Javadoc 和來源 JAR,您可以設定新增的功能,以便為 Javadoc 和來源產生 JAR。

build.gradle.kts
java {
    registerFeature("mongodbSupport") {
        usingSourceSet(sourceSets["mongodbSupport"])
        withJavadocJar()
        withSourcesJar()
    }
}
build.gradle
java {
    registerFeature('mongodbSupport') {
        usingSourceSet(sourceSets.mongodbSupport)
        withJavadocJar()
        withSourcesJar()
    }
}

相依於功能

如前所述,發佈時功能可能會造成資料遺失。因此,消費者只能在下列情況下相依於功能

  • 使用專案相依性 (在多專案建置中)

  • 有 Gradle 模組元資料可用,也就是說發佈者必須已發佈

  • 在 Ivy 世界中,透過宣告相依於與功能相符的組態

消費者可以透過宣告必要的相容性,指定需要生產者的特定功能。例如,如果生產者宣告「MySQL 支援」功能如下

build.gradle.kts
group = "org.gradle.demo"

sourceSets {
    create("mysqlSupport") {
        java {
            srcDir("src/mysql/java")
        }
    }
}

java {
    registerFeature("mysqlSupport") {
        usingSourceSet(sourceSets["mysqlSupport"])
    }
}

dependencies {
    "mysqlSupportImplementation"("mysql:mysql-connector-java:8.0.14")
}
build.gradle
group = 'org.gradle.demo'

sourceSets {
    mysqlSupport {
        java {
            srcDir 'src/mysql/java'
        }
    }
}

java {
    registerFeature('mysqlSupport') {
        usingSourceSet(sourceSets.mysqlSupport)
    }
}

dependencies {
    mysqlSupportImplementation 'mysql:mysql-connector-java:8.0.14'
}

那麼消費者可以透過執行下列動作,宣告相依於 MySQL 支援功能

build.gradle.kts
dependencies {
    // This project requires the main producer component
    implementation(project(":producer"))

    // But we also want to use its MySQL support
    runtimeOnly(project(":producer")) {
        capabilities {
            requireCapability("org.gradle.demo:producer-mysql-support")
        }
    }
}
build.gradle
dependencies {
    // This project requires the main producer component
    implementation(project(":producer"))

    // But we also want to use its MySQL support
    runtimeOnly(project(":producer")) {
        capabilities {
            requireCapability("org.gradle.demo:producer-mysql-support")
        }
    }
}

這會自動在執行時期類別路徑中加入 mysql-connector-java 相依性。如果有超過一個相依性,所有相依性都會加入,這表示功能可用於將有助於功能的相依性分組在一起。

類似地,如果具有功能的外部函式庫已使用 Gradle 模組元資料 發佈,則可以依賴該函式庫提供的功能

build.gradle.kts
dependencies {
    // This project requires the main producer component
    implementation("org.gradle.demo:producer:1.0")

    // But we also want to use its MongoDB support
    runtimeOnly("org.gradle.demo:producer:1.0") {
        capabilities {
            requireCapability("org.gradle.demo:producer-mongodb-support")
        }
    }
}
build.gradle
dependencies {
    // This project requires the main producer component
    implementation('org.gradle.demo:producer:1.0')

    // But we also want to use its MongoDB support
    runtimeOnly('org.gradle.demo:producer:1.0') {
        capabilities {
            requireCapability("org.gradle.demo:producer-mongodb-support")
        }
    }
}

處理互斥變異

功能用作處理功能的方式的主要優點是,您可以精確地處理變異的相容性。規則很簡單

依賴圖形中沒有兩個變異可以提供相同的功能

我們可以利用這一點來確保 Gradle 在使用者錯誤設定相依項時失敗。假設您的函式庫支援 MySQL、Postgres 和 MongoDB,但一次只能選擇其中一個。我們可以透過確保每個功能也提供相同功能來模擬此限制,因此這些功能無法在同一個圖表中一起使用。

build.gradle.kts
java {
    registerFeature("mysqlSupport") {
        usingSourceSet(sourceSets["mysqlSupport"])
        capability("org.gradle.demo", "producer-db-support", "1.0")
        capability("org.gradle.demo", "producer-mysql-support", "1.0")
    }
    registerFeature("postgresSupport") {
        usingSourceSet(sourceSets["postgresSupport"])
        capability("org.gradle.demo", "producer-db-support", "1.0")
        capability("org.gradle.demo", "producer-postgres-support", "1.0")
    }
    registerFeature("mongoSupport") {
        usingSourceSet(sourceSets["mongoSupport"])
        capability("org.gradle.demo", "producer-db-support", "1.0")
        capability("org.gradle.demo", "producer-mongo-support", "1.0")
    }
}

dependencies {
    "mysqlSupportImplementation"("mysql:mysql-connector-java:8.0.14")
    "postgresSupportImplementation"("org.postgresql:postgresql:42.2.5")
    "mongoSupportImplementation"("org.mongodb:mongodb-driver-sync:3.9.1")
}
build.gradle
java {
    registerFeature('mysqlSupport') {
        usingSourceSet(sourceSets.mysqlSupport)
        capability('org.gradle.demo', 'producer-db-support', '1.0')
        capability('org.gradle.demo', 'producer-mysql-support', '1.0')
    }
    registerFeature('postgresSupport') {
        usingSourceSet(sourceSets.postgresSupport)
        capability('org.gradle.demo', 'producer-db-support', '1.0')
        capability('org.gradle.demo', 'producer-postgres-support', '1.0')
    }
    registerFeature('mongoSupport') {
        usingSourceSet(sourceSets.mongoSupport)
        capability('org.gradle.demo', 'producer-db-support', '1.0')
        capability('org.gradle.demo', 'producer-mongo-support', '1.0')
    }
}

dependencies {
    mysqlSupportImplementation 'mysql:mysql-connector-java:8.0.14'
    postgresSupportImplementation 'org.postgresql:postgresql:42.2.5'
    mongoSupportImplementation 'org.mongodb:mongodb-driver-sync:3.9.1'
}

在此,產生器宣告 3 個功能,每個功能支援一個資料庫執行時間

  • mysql-support 提供 db-supportmysql-support 功能

  • postgres-support 提供 db-supportpostgres-support 功能

  • mongo-support 提供 db-supportmongo-support 功能

然後,如果使用者嘗試取得 postgres-supportmysql-support 功能(這也適用於遞移)

build.gradle.kts
dependencies {
    // This project requires the main producer component
    implementation(project(":producer"))

    // Let's try to ask for both MySQL and Postgres support
    runtimeOnly(project(":producer")) {
        capabilities {
            requireCapability("org.gradle.demo:producer-mysql-support")
        }
    }
    runtimeOnly(project(":producer")) {
        capabilities {
            requireCapability("org.gradle.demo:producer-postgres-support")
        }
    }
}
build.gradle
dependencies {
    implementation(project(":producer"))

    // Let's try to ask for both MySQL and Postgres support
    runtimeOnly(project(":producer")) {
        capabilities {
            requireCapability("org.gradle.demo:producer-mysql-support")
        }
    }
    runtimeOnly(project(":producer")) {
        capabilities {
            requireCapability("org.gradle.demo:producer-postgres-support")
        }
    }
}

相依項解析會失敗,並顯示以下錯誤

Cannot choose between
   org.gradle.demo:producer:1.0 variant mysqlSupportRuntimeElements and
   org.gradle.demo:producer:1.0 variant postgresSupportRuntimeElements
   because they provide the same capability: org.gradle.demo:producer-db-support:1.0