使用解析規則
Gradle 提供了幾種機制來直接影響相依性解析引擎的行為。
通常建議僅在其他方法不足時才求助於解析規則。 |
以下是 Gradle 中的主要解析策略
# | 策略 | 資訊 |
---|---|---|
強制相依性版本 |
強制特定版本的相依性。 |
|
模組替換 |
用另一個模組替換一個模組,並附帶說明。 |
|
相依性取代 |
動態取代相依性。 |
|
組件選擇規則 |
控制允許哪些模組版本。拒絕已知損壞或不理想的特定版本。 |
|
預設相依性 |
當未明確宣告相依性時,自動將相依性新增至配置。 |
|
排除遞移性相依性 |
排除您不希望包含在相依性圖表中的遞移性相依性。 |
|
強制解析失敗策略 |
在解析期間發生特定情況時,強制建置失敗。 |
|
停用遞移性相依性 |
相依性預設為遞移性的,但您可以為個別相依性停用此行為。 |
|
相依性解析規則和其他條件 |
直接轉換或篩選已解析的相依性和其他邊緣案例情況。 |
1. 強制相依性版本
您可以強制執行特定版本的相依性,無論建置腳本的其他部分可能請求或解析哪些版本。
這對於確保一致性並避免因使用相同相依性的不同版本而引起的衝突非常有用。
configurations {
"compileClasspath" {
resolutionStrategy.force("commons-codec:commons-codec:1.9")
}
}
dependencies {
implementation("org.apache.httpcomponents:httpclient:4.5.4")
}
configurations {
compileClasspath {
resolutionStrategy.force 'commons-codec:commons-codec:1.9'
}
}
dependencies {
implementation 'org.apache.httpcomponents:httpclient:4.5.4'
}
2. 模組替換
雖然通常最好使用功能來管理模組衝突,但在某些情況下(尤其是在使用舊版 Gradle 時)需要不同的方法。在這些情況下,模組替換規則透過允許您指定舊版函式庫已被較新的函式庫取代來提供解決方案。
模組替換規則允許您宣告舊版函式庫已被較新的函式庫取代。例如,從 google-collections
移轉到 guava
涉及將模組從 com.google.collections:google-collections
重新命名為 com.google.guava:guava
。此類變更會影響衝突解決,因為 Gradle 不會將它們視為因模組座標不同而導致的版本衝突。
考慮一個場景,其中兩個函式庫都出現在相依性圖表中。您的專案相依於 guava
,但遞移性相依性會拉入 google-collections
。這可能會導致執行階段錯誤,因為 Gradle 不會自動將此解析為衝突。常見的解決方案包括
-
宣告排除規則以避免
google-collections
。 -
避免拉入舊版函式庫的相依性。
-
升級不再使用
google-collections
的相依性。 -
降級到
google-collections
(不建議)。 -
指派功能,以便
google-collections
和guava
互斥。
這些方法可能不足以應對大型專案。透過宣告模組替換,您可以跨專案全域解決此問題,從而使組織能夠整體處理此類衝突。
dependencies {
modules {
module("com.google.collections:google-collections") {
replacedBy("com.google.guava:guava", "google-collections is now part of Guava")
}
}
}
dependencies {
modules {
module("com.google.collections:google-collections") {
replacedBy("com.google.guava:guava", "google-collections is now part of Guava")
}
}
}
宣告後,Gradle 會在衝突解決期間將任何版本的 guava
視為優於 google-collections
,從而確保只有 guava
出現在類別路徑中。但是,如果 google-collections
是唯一存在的模組,則除非發生衝突,否則不會自動替換它。
如需更多範例,請參閱 ComponentMetadataHandler 的 DSL 參考。
Gradle 目前不支援用多個模組替換一個模組,但多個模組可以替換為單個模組。 |
3. 相依性取代
相依性取代規則允許用指定的替代方案替換專案和模組相依性,使它們可以互換。雖然與相依性解析規則類似,但它們透過啟用專案和模組相依性之間的替換來提供更大的彈性。
但是,新增相依性取代規則會影響配置解析的時機。配置不是在首次使用時解析,而是在工作圖表建構期間解析,如果配置稍後被修改或相依於在工作執行期間發佈的模組,則可能會導致問題。
說明
-
配置可以用作工作的輸入,並在解析時包含專案相依性。
-
如果專案相依性是工作的輸入(透過配置),則會將建置這些成品的工作新增為相依性。
-
為了確定作為工作輸入的專案相依性,Gradle 必須解析配置輸入。
-
由於 Gradle 工作圖表一旦工作執行開始就固定下來,因此 Gradle 需要在執行任何工作之前執行此解析。
在沒有取代規則的情況下,Gradle 假設外部模組相依性不參考專案相依性,從而簡化了相依性遍歷。使用取代規則,此假設不再成立,因此 Gradle 必須完全解析配置以確定專案相依性。
用專案相依性取代外部模組相依性
相依性取代可用於用本機開發的專案替換外部模組,這在測試模組的修補或未發行版本時很有用。
無論是否指定版本,都可以替換外部模組
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(module("org.utils:api"))
.using(project(":api")).because("we work with the unreleased development version")
substitute(module("org.utils:util:2.5")).using(project(":util"))
}
}
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute module("org.utils:api") using project(":api") because "we work with the unreleased development version"
substitute module("org.utils:util:2.5") using project(":util")
}
}
-
取代的專案必須是多專案建置的一部分(透過
settings.gradle
包含)。 -
取代會用專案相依性替換模組相依性並設定工作相依性,但不會自動將專案包含在建置中。
用模組替換取代專案相依性
您也可以使用取代規則,用多專案建置中的外部模組替換專案相依性。
這種技術可以透過允許從儲存庫下載某些相依性而不是在本機建置它們來加速開發
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(project(":api"))
.using(module("org.utils:api:1.3")).because("we use a stable version of org.utils:api")
}
}
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute project(":api") using module("org.utils:api:1.3") because "we use a stable version of org.utils:api"
}
}
-
取代的模組必須包含版本。
-
即使在取代之後,專案仍然是多專案建置的一部分,但在解析配置時不會執行建置它的工作。
有條件地取代相依性
您可以使用相依性取代規則,在多專案建置中有條件地用本機專案取代模組相依性。
當您想要使用本機開發的相依性版本(如果存在),否則回退到外部模組時,這特別有用
configurations.all {
resolutionStrategy.dependencySubstitution.all {
requested.let {
if (it is ModuleComponentSelector && it.group == "org.example") {
val targetProject = findProject(":${it.module}")
if (targetProject != null) {
useTarget(targetProject)
}
}
}
}
}
configurations.all {
resolutionStrategy.dependencySubstitution.all { DependencySubstitution dependency ->
if (dependency.requested instanceof ModuleComponentSelector && dependency.requested.group == "org.example") {
def targetProject = findProject(":${dependency.requested.module}")
if (targetProject != null) {
dependency.useTarget targetProject
}
}
}
}
-
僅當找到與相依性名稱相符的本機專案時,才會發生取代。
-
本機專案必須已包含在多專案建置中(透過
settings.gradle
)。
用另一個變體取代相依性
您可以用另一個變體取代相依性,例如在平台相依性和常規函式庫相依性之間切換。
當您的建置過程需要根據特定條件變更相依性類型時,這非常有用
configurations.all {
resolutionStrategy.dependencySubstitution {
all {
if (requested is ModuleComponentSelector && requested.group == "org.example" && requested.version == "1.0") {
useTarget(module("org.example:library:1.0")).because("Switching from platform to library variant")
}
}
}
}
-
取代基於請求的相依性的屬性(例如群組和版本)。
-
此方法允許您從平台組件切換到函式庫,反之亦然。
-
它使用 Gradle 的變體感知引擎,以確保根據配置和消費者屬性選擇正確的變體。
當處理複雜的相依性圖表(其中需要動態交換不同的組件類型(平台、函式庫))時,通常需要這種彈性。
用屬性取代相依性
基於屬性取代相依性允許您透過鎖定特定屬性(例如平台與常規函式庫)來覆寫組件的預設選擇。
此技術適用於管理複雜建置中的平台和函式庫相依性,尤其是在您想要使用常規函式庫但平台相依性宣告不正確時
dependencies {
// This is a platform dependency but you want the library
implementation(platform("com.google.guava:guava:28.2-jre"))
}
dependencies {
// This is a platform dependency but you want the library
implementation platform('com.google.guava:guava:28.2-jre')
}
在此範例中,取代規則鎖定 com.google.guava:guava
的平台版本,並用常規函式庫版本替換它
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(platform(module("com.google.guava:guava:28.2-jre")))
.using(module("com.google.guava:guava:28.2-jre"))
}
}
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(platform(module('com.google.guava:guava:28.2-jre'))).
using module('com.google.guava:guava:28.2-jre')
}
}
如果沒有 platform
關鍵字,則取代不會特別鎖定平台相依性。
以下規則執行相同的取代,但使用更精細的變體標記法,允許自訂相依性的屬性
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(variant(module("com.google.guava:guava:28.2-jre")) {
attributes {
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.REGULAR_PLATFORM))
}
}).using(module("com.google.guava:guava:28.2-jre"))
}
}
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute variant(module('com.google.guava:guava:28.2-jre')) {
attributes {
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.REGULAR_PLATFORM))
}
} using module('com.google.guava:guava:28.2-jre')
}
}
透過使用基於屬性的取代,您可以精確控制要替換哪些相依性,從而確保 Gradle 在您的建置中解析正確的版本和變體。
有關完整參考,請參閱 DependencySubstitutions API。
在複合建置中,您必須比對確切請求的相依性屬性的規則不適用。使用複合建置時,Gradle 將自動比對請求的屬性。換句話說,如果您包含另一個建置,則隱含地表示您正在用包含建置中等效的變體取代已取代模組的所有變體。 |
用具有功能的相依性取代相依性
您可以用包含特定功能的不同變體取代相依性。功能允許您指定相依性的特定變體提供一組相關的功能或特性,例如測試組件。
此範例使用功能,用其測試組件取代常規相依性
configurations.testCompileClasspath {
resolutionStrategy.dependencySubstitution {
substitute(module("com.acme:lib:1.0")).using(variant(module("com.acme:lib:1.0")) {
capabilities {
requireCapability("com.acme:lib-test-fixtures")
}
})
}
}
configurations.testCompileClasspath {
resolutionStrategy.dependencySubstitution {
substitute(module('com.acme:lib:1.0'))
.using variant(module('com.acme:lib:1.0')) {
capabilities {
requireCapability('com.acme:lib-test-fixtures')
}
}
}
}
在這裡,我們用其 lib-test-fixtures
變體取代常規 com.acme:lib:1.0
相依性。requireCapability
函數指定新變體必須具有 com.acme:lib-test-fixtures
功能,以確保為測試目的選擇正確版本的相依性。
取代規則中的功能用於精確比對相依性,並且 Gradle 僅取代與所需功能相符的相依性。
有關變體取代 API 的完整參考,請參閱 DependencySubstitutions API。
用分類器或成品取代相依性
您可以用沒有分類器的相依性或反之亦然,取代具有分類器的相依性。分類器通常用於表示同一成品的不同版本,例如平台特定的建置或具有不同 API 的相依性。雖然 Gradle 不鼓勵使用分類器,但它提供了一種處理仍然使用分類器的情況下的取代方法。
考慮以下設定
dependencies {
implementation("com.google.guava:guava:28.2-jre")
implementation("co.paralleluniverse:quasar-core:0.8.0")
implementation(project(":lib"))
}
dependencies {
implementation 'com.google.guava:guava:28.2-jre'
implementation 'co.paralleluniverse:quasar-core:0.8.0'
implementation project(':lib')
}
在上面的範例中,對 quasar
的第一層相依性讓我們認為 Gradle 會解析 quasar-core-0.8.0.jar
,但事實並非如此。
建置失敗,並顯示以下訊息
Execution failed for task ':consumer:resolve'.
> Could not resolve all files for configuration ':consumer:runtimeClasspath'.
> Could not find quasar-core-0.8.0-jdk8.jar (co.paralleluniverse:quasar-core:0.8.0).
Searched in the following locations:
https://repo.maven.apache.org/maven2/co/paralleluniverse/quasar-core/0.8.0/quasar-core-0.8.0-jdk8.jar
這是因為另一個專案 lib
存在相依性,而該專案本身相依於不同版本的 quasar-core
dependencies {
implementation("co.paralleluniverse:quasar-core:0.7.10:jdk8")
}
dependencies {
implementation "co.paralleluniverse:quasar-core:0.7.10:jdk8"
}
-
消費者相依於沒有分類器的
quasar-core:0.8.0
。 -
lib 專案相依於具有
jdk8
分類器的quasar-core:0.7.10
。 -
Gradle 的衝突解決選擇較高版本 (
0.8.0
),但quasar-core:0.8.0
沒有jdk8
分類器,從而導致解析錯誤。
為了解決此衝突,您可以指示 Gradle 在解析 quasar-core
相依性時忽略分類器
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(module("co.paralleluniverse:quasar-core"))
.using(module("co.paralleluniverse:quasar-core:0.8.0"))
.withoutClassifier()
}
}
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute module('co.paralleluniverse:quasar-core') using module('co.paralleluniverse:quasar-core:0.8.0') withoutClassifier()
}
}
此規則有效地將圖表中找到的任何 quasar-core
相依性替換為沒有分類器的相依性。
如果您需要用特定的分類器或成品取代,您可以在取代規則中指定分類器或成品詳細資訊。
如需更詳細的資訊,請參閱
-
透過 Substitution DSL 選擇成品
-
透過 DependencySubstitution API 選擇成品
-
透過 ResolutionStrategy API 選擇成品
4. 組件選擇規則
當有多個版本可供選擇以符合版本選擇器時,組件選擇規則可能會影響應選擇哪個組件實例。規則會針對每個可用版本套用,並允許明確拒絕該版本。
這允許 Gradle 忽略任何不符合規則設定條件的組件實例。範例包括
-
對於像
1.+
這樣的動態版本,某些版本可能會被明確拒絕選擇。 -
對於像
1.4
這樣的靜態版本,可以基於額外的組件中繼資料(例如 Ivy 分支屬性)拒絕實例,從而允許使用後續儲存庫中的實例。
規則透過 ComponentSelectionRules 物件配置。配置的每個規則都將使用 ComponentSelection 物件作為引數呼叫,該物件包含有關正在考慮的候選版本的資訊。呼叫 ComponentSelection.reject(java.lang.String) 會導致明確拒絕給定的候選版本,在這種情況下,候選版本將不被視為選擇器。
以下範例顯示了一個規則,該規則不允許特定版本的模組,但允許動態版本選擇下一個最佳候選版本
configurations {
implementation {
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 {
implementation("org.sample:api:1.+")
}
configurations {
implementation {
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 {
implementation 'org.sample:api:1.+'
}
請注意,版本選擇是從最高版本開始套用的。選擇的版本將是找到的所有組件選擇規則都接受的第一個版本。
如果沒有規則明確拒絕版本,則該版本被視為已接受。 |
同樣,規則也可以針對特定模組。模組必須以 group:module
的形式指定
configurations {
create("targetConfig") {
resolutionStrategy {
componentSelection {
withModule("org.sample:api") {
if (candidate.version == "1.5") {
reject("version 1.5 is broken for 'org.sample:api'")
}
}
}
}
}
}
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'")
}
}
}
}
}
}
組件選擇規則也可以在選擇版本時考慮組件中繼資料。可以考慮的其他可能的中繼資料是 ComponentMetadata 和 IvyModuleDescriptor。
請注意,此額外資訊可能並不總是可用,因此應檢查 null
值
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")
}
}
}
}
}
}
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 引數作為參數。
5. 預設相依性
您可以為配置設定預設相依性,以確保在未指定明確相依性時使用預設版本。
這對於依賴於版本化工具的外掛程式非常有用,並且希望在使用者未指定版本時提供預設值
configurations {
create("pluginTool") {
defaultDependencies {
add(project.dependencies.create("org.gradle:my-util:1.0"))
}
}
}
configurations {
pluginTool {
defaultDependencies { dependencies ->
dependencies.add(project.dependencies.create("org.gradle:my-util:1.0"))
}
}
}
在此範例中,除非指定了另一個版本,否則 pluginTool
配置將使用 org.gradle:my-util:1.0
作為預設相依性。
6. 排除遞移性相依性
若要完全排除特定配置的遞移性相依性,請使用 Configuration.exclude(Map)
方法。
此方法將自動從配置中宣告的所有相依性中排除指定的遞移性相依性
configurations {
"implementation" {
exclude(group = "commons-collections", module = "commons-collections")
}
}
dependencies {
implementation("commons-beanutils:commons-beanutils:1.9.4")
implementation("com.opencsv:opencsv:4.6")
}
configurations {
implementation {
exclude group: 'commons-collections', module: 'commons-collections'
}
}
dependencies {
implementation 'commons-beanutils:commons-beanutils:1.9.4'
implementation 'com.opencsv:opencsv:4.6'
}
在此範例中,無論 commons-collections
相依性是直接相依性還是遞移性相依性,都將從 implementation
配置中排除。
7. 強制解析失敗策略
版本衝突可以強制使用以下方法失敗
-
failOnNonReproducibleResolution()
-
failOnDynamicVersions()
-
failOnChangingVersions()
-
failOnVersionConflict()
當找到同一相依性的衝突版本時,這將使建置失敗
configurations.all {
resolutionStrategy {
failOnVersionConflict()
}
}
configurations.all {
resolutionStrategy {
failOnVersionConflict()
}
}
8. 停用遞移性相依性
預設情況下,Gradle 會解析給定模組的所有遞移性相依性。
但是,在某些情況下,您可能想要停用此行為,例如當您需要更多地控制相依性或相依性中繼資料不正確時。
您可以透過將 ModuleDependency.setTransitive(boolean) 設定為 false
,告訴 Gradle 停用相依性的遞移性相依性管理。
在以下範例中,為 guava
相依性停用了遞移性相依性解析
dependencies {
implementation("com.google.guava:guava:23.0") {
isTransitive = false
}
}
dependencies {
implementation('com.google.guava:guava:23.0') {
transitive = false
}
}
這確保僅解析 guava
的主要成品,並且不會包含其任何遞移性相依性。
停用遞移性相依性解析可能會要求您在建置腳本中宣告必要的執行階段相依性,否則這些相依性將自動解析。不這樣做可能會導致執行階段類別路徑問題。 |
如果您想要全域停用所有相依性的遞移性解析,您可以在配置層級設定此行為
configurations.all {
isTransitive = false
}
dependencies {
implementation("com.google.guava:guava:23.0")
}
configurations.all {
transitive = false
}
dependencies {
implementation 'com.google.guava:guava:23.0'
}
這會停用專案中所有相依性的遞移性解析。請注意,這可能要求您手動宣告執行階段所需的任何遞移性相依性。
如需更多資訊,請參閱 Configuration.setTransitive(boolean)。
9. 相依性解析規則和其他條件
相依性解析規則在解析每個相依性時執行,從而提供強大的 API 來修改相依性的屬性(例如群組、名稱或版本),然後再最終確定解析。
這允許對相依性解析進行進階控制,使您能夠在解析過程中用一個模組替換另一個模組。
此功能對於實作進階相依性管理模式特別有用。透過相依性解析規則,您可以將相依性重新導向到特定版本,甚至完全不同的模組,從而使您能夠在專案中強制執行一致的版本或覆寫有問題的相依性
configurations.all {
resolutionStrategy {
eachDependency {
if (requested.group == "com.example" && requested.name == "old-library") {
useTarget("com.example:new-library:1.0.0")
because("Our license only allows use of version 1")
}
}
}
}
configurations.all {
resolutionStrategy {
eachDependency {
if (requested.group == "com.example" && requested.name == "old-library") {
useTarget("com.example:new-library:1.0.0")
because("Our license only allows use of version 1")
}
}
}
}
在此範例中,如果請求了 com.example:old-library
的相依性,則會在解析期間將其替換為 com.example:new-library:1.0.0
。
如需更進階的使用方法和其他範例,請參閱 API 文件中的 ResolutionStrategy 類別。
實作自訂版本控制方案
在某些企業環境中,Gradle 建置中的模組版本在外部維護和稽核。相依性解析規則提供了一種實作此目的的有效方法
-
開發人員在建置腳本中使用模組的群組和名稱宣告相依性,但指定一個佔位符版本,例如
default
。 -
然後,相依性解析規則將
default
版本解析為核准的版本,該版本從核准模組的企業目錄中檢索。
此方法可確保僅使用核准的版本,同時允許開發人員使用簡化且一致的版本控制方案。
規則實作可以封裝在企業外掛程式中,使其易於在組織內的所有專案中套用
configurations.all {
resolutionStrategy.eachDependency {
if (requested.version == "default") {
val version = findDefaultVersionInCatalog(requested.group, requested.name)
useVersion(version.version)
because(version.because)
}
}
}
data class DefaultVersion(val version: String, val because: String)
fun findDefaultVersionInCatalog(group: String, name: String): DefaultVersion {
//some custom logic that resolves the default version into a specific version
return DefaultVersion(version = "1.0", because = "tested by QA")
}
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.version == 'default') {
def version = findDefaultVersionInCatalog(details.requested.group, details.requested.name)
details.useVersion version.version
details.because version.because
}
}
}
def findDefaultVersionInCatalog(String group, String name) {
//some custom logic that resolves the default version into a specific version
[version: "1.0", because: 'tested by QA']
}
在此設定中,每當開發人員將 default
指定為版本時,解析規則都會用企業目錄中的核准版本替換它。
此策略可確保符合企業政策,同時為開發人員提供彈性和易用性。將此邏輯封裝在外掛程式中還可以確保多個專案之間的一致性。
替換不需要的相依性版本
相依性解析規則提供了一種強大的機制,用於封鎖相依性的特定版本並用替代版本替換它們。
當已知特定版本存在問題時(例如引入錯誤或依賴於公用儲存庫中不可用的函式庫的版本),這特別有用。透過定義解析規則,您可以自動用穩定的版本替換有問題的版本。
考慮一個場景,其中函式庫的版本 1.2
已損壞,但版本 1.2.1
包含重要的修復程式,並且應始終使用。透過解析規則,您可以強制執行此替換:「任何時候請求版本 1.2
,它都將被替換為 1.2.1
。與強制版本不同,此規則僅影響特定版本 1.2
,而其他版本不受影響
configurations.all {
resolutionStrategy.eachDependency {
if (requested.group == "org.software" && requested.name == "some-library" && requested.version == "1.2") {
useVersion("1.2.1")
because("fixes critical bug in 1.2")
}
}
}
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.group == 'org.software' && details.requested.name == 'some-library' && details.requested.version == '1.2') {
details.useVersion '1.2.1'
details.because 'fixes critical bug in 1.2'
}
}
}
如果版本 1.3
也存在於相依性圖表中,那麼即使有此規則,Gradle 的預設衝突解決策略也會選擇 1.3
作為最新版本。
與豐富版本約束的差異: 使用豐富版本約束,您可以完全拒絕某些版本,從而導致建置失敗,或者在使用動態相依性時選擇未拒絕的版本。相反,像這裡顯示的相依性解析規則會操縱正在請求的版本,在找到拒絕的版本時用已知的良好版本替換它。這種方法是處理拒絕版本的解決方案,而豐富版本約束是關於表達避免某些版本的意圖。
延遲影響已解析的相依性
外掛程式可以透過有條件地新增相依性或在使用者未指定版本時設定慣用版本來延遲影響相依性。
以下是說明這些使用案例的兩個範例。
此範例示範如何根據某些條件(延遲評估)將相依性新增至配置
configurations {
implementation {
dependencies.addLater(project.provider {
val dependencyNotation = conditionalLogic()
if (dependencyNotation != null) {
project.dependencies.create(dependencyNotation)
} else {
null
}
})
}
}
configurations {
implementation {
dependencies.addLater(project.provider {
def dependencyNotation = conditionalLogic()
if (dependencyNotation != null) {
return project.dependencies.create(dependencyNotation)
} else {
return null
}
})
}
}
在本例中,addLater
用於延遲相依性的評估,使其僅在滿足某些條件時才新增。
在本範例中,建置腳本設定了相依性的慣用版本,如果未明確指定版本,則將使用該版本
範例 2:慣用相依性的預設版本
dependencies {
implementation("org:foo")
// Can indiscriminately be added by build logic
constraints {
implementation("org:foo:1.0") {
version {
// Applied to org:foo if no other version is specified
prefer("1.0")
}
}
}
}
dependencies {
implementation("org:foo")
// Can indiscriminately be added by build logic
constraints {
implementation("org:foo:1.0") {
version {
// Applied to org:foo if no other version is specified
prefer("1.0")
}
}
}
}
這確保 org:foo
使用版本 1.0
,除非使用者指定另一個版本。