在許多情況下,您會想要使用特定模組相依性的最新版本,或版本範圍內的最新版本。這可能是開發期間的要求,或者您可能正在開發一個旨在與各種相依性版本搭配使用的函式庫。您可以透過使用動態版本輕鬆依賴這些持續變更的相依性。動態版本可以是版本範圍(例如 2.+),也可以是可用最新版本的佔位符,例如 latest.integration

或者,您要求的模組可能會隨著時間而改變,即使是相同版本,也稱為變更版本。此類變更模組的範例是 Maven SNAPSHOT 模組,它總是指向發布的最新成品。換句話說,標準 Maven 快照是一個持續演進的模組,它是一個「變更模組」。

使用動態版本和變更模組可能會導致無法重製的組建。由於特定模組的新版本已發布,其 API 可能與您的原始碼不相容。請謹慎使用此功能!

宣告動態版本

專案可能會採用更積極的方式來使用模組的相依性。例如,您可能想要隨時整合相依性的最新版本,以隨時使用尖端功能。動態版本允許解析特定模組的最新版本或版本範圍的最新版本。

在建置中使用動態版本可能會造成中斷的風險。一旦相依性的新版本釋出,其中包含不相容的 API 變更,您的原始碼可能會停止編譯。
build.gradle.kts
plugins {
    `java-library`
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework:spring-web:5.+")
}
build.gradle
plugins {
    id 'java-library'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework:spring-web:5.+'
}

建置掃描可以有效視覺化動態相依性版本及其各自選取的版本。

dependency management dynamic dependency build scan
圖 1. 建置掃描中的動態相依性

預設情況下,Gradle 會快取相依性的動態版本 24 小時。在此時間範圍內,Gradle 不會嘗試從宣告的儲存庫解析較新的版本。可以視需要設定臨界值,例如,如果您想要提早解析新版本。

宣告變更版本

團隊可能會決定在釋出應用程式或函式庫的新版本之前,先實作一系列功能。允許使用者提早且頻繁整合未完成版本的成品的常見策略,是釋出具有所謂變更版本的模組。變更版本表示功能集仍處於積極開發中,尚未釋出一般可用的穩定版本。

在 Maven 儲存庫中,變更版本通常稱為快照版本。快照版本包含字尾 -SNAPSHOT。下列範例示範如何宣告 Spring 相依性的快照版本。

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

repositories {
    mavenCentral()
    maven {
        url = uri("https://repo.spring.io/snapshot/")
    }
}

dependencies {
    implementation("org.springframework:spring-web:5.0.3.BUILD-SNAPSHOT")
}
build.gradle
plugins {
    id 'java-library'
}

repositories {
    mavenCentral()
    maven {
        url 'https://repo.spring.io/snapshot/'
    }
}

dependencies {
    implementation 'org.springframework:spring-web:5.0.3.BUILD-SNAPSHOT'
}

預設情況下,Gradle 會快取變更版本的相依項 24 小時。在此時間範圍內,Gradle 不會嘗試從宣告的儲存庫解析較新的版本。閾值可以依需要進行設定,例如如果您想提早解析新的快照版本。

Gradle 夠靈活,可以將任何版本視為變更版本,例如如果您想為 Ivy 模組建模快照行為。您只需將屬性 ExternalModuleDependency.setChanging(boolean) 設定為 true 即可。

控制動態版本快取

預設情況下,Gradle 會快取動態版本和變更模組 24 小時。在此時間範圍內,Gradle 不會與任何宣告的遠端儲存庫聯繫以取得新版本。如果您希望 Gradle 更頻繁地檢查遠端儲存庫,或在每次執行您的建置時檢查,則需要變更生存時間 (TTL) 閾值。

對動態或變更版本使用較短的 TTL 閾值可能會因為 HTTP(s) 呼叫次數增加而導致建置時間變長。

您可以使用 命令列選項 覆寫預設快取模式。您也可以使用解析策略在建置程式中 變更快取過期時間

以程式方式控制相依項快取

您可以使用設定檔的 ResolutionStrategy 以程式方式微調快取的特定面向。如果您想要永久變更設定,以程式方式進行會很有用。

預設情況下,Gradle 會快取動態版本 24 小時。若要變更 Gradle 快取動態版本解析版本的時間長度,請使用

build.gradle.kts
configurations.all {
    resolutionStrategy.cacheDynamicVersionsFor(10, "minutes")
}
build.gradle
configurations.all {
    resolutionStrategy.cacheDynamicVersionsFor 10, 'minutes'
}

預設情況下,Gradle 會快取變更模組 24 小時。若要變更 Gradle 快取變更模組的元資料和人工製品的時間長度,請使用

build.gradle.kts
configurations.all {
    resolutionStrategy.cacheChangingModulesFor(4, "hours")
}
build.gradle
configurations.all {
    resolutionStrategy.cacheChangingModulesFor 4, 'hours'
}

從命令列控制相依項快取

避免網路存取與離線模式

--offline 命令列開關會指示 Gradle 永遠使用快取中的相依模組,而不管是否需要再次檢查。在離線執行時,Gradle 永遠不會嘗試存取網路來執行相依解析。如果相依快取中沒有必要的模組,建置執行會失敗。

重新整理相依

您可以從命令列控制相依快取在特定建置呼叫中的行為。命令列選項有助於在單一建置執行中做出選擇性的臨時選擇。

有時,Gradle 相依快取會與設定儲存庫的實際狀態不同步。可能是儲存庫最初設定錯誤,或可能是「不變更」模組發布錯誤。若要重新整理相依快取中的所有相依,請在命令列上使用 --refresh-dependencies 選項。

--refresh-dependencies 選項會指示 Gradle 忽略已解析模組和人工製品的所有快取項目。系統會針對所有設定的儲存庫執行新的解析,重新計算動態版本、重新整理模組和下載人工製品。不過,在可能的情況下,Gradle 會在再次下載之前檢查先前下載的人工製品是否有效。這會透過將儲存庫中發布的 SHA1 值與現有已下載人工製品的 SHA1 值進行比較來完成。

  • 動態相依的新版本

  • 變更模組的新版本(使用相同版本字串但內容可能不同的模組)

重新整理相依會導致 Gradle 宣告其清單快取無效。不過

  • 它會對元資料檔執行 HTTP HEAD 要求,但如果它們相同,不會重新下載它們

  • 它會對人工製品檔執行 HTTP HEAD 要求,但如果它們相同,不會重新下載它們

換句話說,重新整理相依只有在您實際使用動態相依您有未察覺的變更相依(在這種情況下,您有責任正確地向 Gradle 宣告它們為變更相依)時才會產生影響。

認為使用 --refresh-dependencies 會強制下載相依是一個常見的誤解。並非如此:Gradle 只會執行重新整理動態相依所絕對需要的動作。這可能涉及下載新的清單或元資料檔,甚至人工製品,但如果沒有變更,影響就會很小。

使用元件選取規則

元件選取規則可能會影響在多個符合版本選擇器的版本可用時,應該選取哪個元件實例。規則會套用至每個可用的版本,並允許版本透過規則明確地被拒絕。這允許 Gradle 忽略任何不符合規則設定條件的元件實例。範例包括

  • 對於動態版本(例如 1.+),某些版本可能會明確地從選取中拒絕。

  • 對於靜態版本(例如 1.4),可能會根據額外的元件元資料(例如 Ivy 分支屬性)拒絕實例,允許使用來自後續儲存庫的實例。

規則透過 ComponentSelectionRules 物件進行設定。設定的每個規則會以 ComponentSelection 物件作為引數呼叫,其中包含有關所考慮的候選版本的資訊。呼叫 ComponentSelection.reject(java.lang.String) 會導致明確地拒絕指定的候選版本,在這種情況下,候選版本將不會被選擇器考慮。

以下範例顯示一個規則,禁止使用模組的特定版本,但允許動態版本選取下一個最佳候選版本。

build.gradle.kts
configurations {
    create("rejectConfig") {
        resolutionStrategy {
            componentSelection {
                // Accept the highest version matching the requested version that isn't '1.5'
                all {
                    if (candidate.group == "org.sample" && candidate.module == "api" && candidate.version == "1.5") {
                        reject("version 1.5 is broken for 'org.sample:api'")
                    }
                }
            }
        }
    }
}

dependencies {
    "rejectConfig"("org.sample:api:1.+")
}
build.gradle
configurations {
    rejectConfig {
        resolutionStrategy {
            componentSelection {
                // Accept the highest version matching the requested version that isn't '1.5'
                all { ComponentSelection selection ->
                    if (selection.candidate.group == 'org.sample' && selection.candidate.module == 'api' && selection.candidate.version == '1.5') {
                        selection.reject("version 1.5 is broken for 'org.sample:api'")
                    }
                }
            }
        }
    }
}

dependencies {
    rejectConfig "org.sample:api:1.+"
}

請注意,版本選取會從最高版本開始套用。選取的版本將會是所有元件選取規則都接受的第一個版本。如果沒有規則明確地拒絕版本,則該版本會被視為已接受。

類似地,規則可以針對特定模組。模組必須以 group:module 的形式指定。

build.gradle.kts
configurations {
    create("targetConfig") {
        resolutionStrategy {
            componentSelection {
                withModule("org.sample:api") {
                    if (candidate.version == "1.5") {
                        reject("version 1.5 is broken for 'org.sample:api'")
                    }
                }
            }
        }
    }
}
build.gradle
configurations {
    targetConfig {
        resolutionStrategy {
            componentSelection {
                withModule("org.sample:api") { ComponentSelection selection ->
                    if (selection.candidate.version == "1.5") {
                        selection.reject("version 1.5 is broken for 'org.sample:api'")
                    }
                }
            }
        }
    }
}

元件選取規則在選取版本時也可以考慮元件元資料。可以考慮的額外元資料可能是 ComponentMetadataIvyModuleDescriptor。請注意,這些額外資訊可能並不總是可用,因此應該檢查 null 值。

build.gradle.kts
configurations {
    create("metadataRulesConfig") {
        resolutionStrategy {
            componentSelection {
                // Reject any versions with a status of 'experimental'
                all {
                    if (candidate.group == "org.sample" && metadata?.status == "experimental") {
                        reject("don't use experimental candidates from 'org.sample'")
                    }
                }
                // Accept the highest version with either a "release" branch or a status of 'milestone'
                withModule("org.sample:api") {
                    if (getDescriptor(IvyModuleDescriptor::class)?.branch != "release" && metadata?.status != "milestone") {
                        reject("'org.sample:api' must have testing branch or milestone status")
                    }
                }
            }
        }
    }
}
build.gradle
configurations {
    metadataRulesConfig {
        resolutionStrategy {
            componentSelection {
                // Reject any versions with a status of 'experimental'
                all { ComponentSelection selection ->
                    if (selection.candidate.group == 'org.sample' && selection.metadata?.status == 'experimental') {
                        selection.reject("don't use experimental candidates from 'org.sample'")
                    }
                }
                // Accept the highest version with either a "release" branch or a status of 'milestone'
                withModule('org.sample:api') { ComponentSelection selection ->
                    if (selection.getDescriptor(IvyModuleDescriptor)?.branch != "release" && selection.metadata?.status != 'milestone') {
                        selection.reject("'org.sample:api' must be a release branch or have milestone status")
                    }
                }
            }
        }
    }
}

請注意,在宣告元件選取規則時,ComponentSelection 引數總是需要作為參數。