Gradle 提供了几种机制来直接影响依赖解析引擎的行为。

依赖约束组件元数据规则不同,后者作为解析过程的输入,这些机制允许您将规则直接注入到解析引擎中。由于它们的直接影响,它们可以被认为是可能掩盖潜在问题的强力解决方案,例如引入新的依赖项。

通常建议仅在其他方法不足时才求助于解析规则

如果您正在开发,则最好使用依赖约束,因为它们与您的使用者共享。

以下是 Gradle 中的关键解析策略

# 策略 信息

1

强制依赖版本

强制指定依赖项的特定版本。

2

模块替换

用解释替换一个模块为另一个模块。

3

依赖替换

动态替换依赖项。

4

组件选择规则

控制允许使用的模块版本。拒绝已知损坏或不需要的特定版本。

5

默认依赖

当没有显式声明依赖项时,自动向配置添加依赖项。

6

排除传递依赖

排除您不希望包含在依赖关系图中的传递依赖项。

7

强制失败解析策略

当解析期间发生某些情况时,强制构建失败。

8

禁用传递依赖

默认情况下,依赖项是传递的,但您可以为单个依赖项禁用此行为。

9

依赖解析规则和其他条件

直接转换或过滤已解析的依赖项以及其他极端情况。

1. 强制依赖版本

您可以强制执行依赖项的特定版本,而无需考虑构建脚本的其他部分可能请求或解析的版本。

这对于确保一致性并避免因使用相同依赖项的不同版本而引起的冲突非常有用。

build.gradle.kts
configurations {
    "compileClasspath" {
        resolutionStrategy.force("commons-codec:commons-codec:1.9")
    }
}

dependencies {
    implementation("org.apache.httpcomponents:httpclient:4.5.4")
}
build.gradle
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-collectionsguava 互斥。

对于大型项目,这些方法可能不足。通过声明模块替换,您可以全局地解决整个项目中的此问题,从而使组织能够整体处理此类冲突。

build.gradle.kts
dependencies {
    modules {
        module("com.google.collections:google-collections") {
            replacedBy("com.google.guava:guava", "google-collections is now part of Guava")
        }
    }
}
build.gradle
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 必须完全解析配置以确定项目依赖项。

用项目依赖项替换外部模块依赖项

依赖替换可用于用本地开发的项目替换外部模块,这在测试模块的修补或未发布版本时很有用。

无论是否指定版本,都可以替换外部模块

build.gradle.kts
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"))
    }
}
build.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")
    }
}
  • 替换的项目必须是多项目构建的一部分(通过 settings.gradle 包含)。

  • 替换会将模块依赖项替换为项目依赖项并设置任务依赖项,但不会自动将项目包含在构建中。

用模块替换替换项目依赖项

您还可以使用替换规则在多项目构建中用外部模块替换项目依赖项。

此技术可以通过允许从仓库下载某些依赖项而不是在本地构建来加速开发

build.gradle.kts
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute(project(":api"))
            .using(module("org.utils:api:1.3")).because("we use a stable version of org.utils:api")
    }
}
build.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"
    }
}
  • 替换的模块必须包含版本。

  • 即使在替换之后,项目仍然是多项目构建的一部分,但在解析配置时不会执行构建它的任务。

有条件地替换依赖项

您可以使用依赖替换规则在多项目构建中有条件地用本地项目替换模块依赖项。

当您想使用本地开发的依赖项版本(如果存在),否则回退到外部模块时,这特别有用

build.gradle.kts
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)
                }
            }
        }
    }
}
build.gradle
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")
            }
        }
    }
}
  • 替换基于请求的依赖项的属性(如 group 和 version)。

  • 这种方法允许您从平台组件切换到库,反之亦然。

  • 它使用 Gradle 的变体感知引擎来确保根据配置和使用者属性选择正确的变体。

当处理复杂的依赖关系图时,通常需要这种灵活性,在这些图中,不同的组件类型(平台、库)需要动态交换。

用属性替换依赖项

基于属性替换依赖项允许您通过定位特定属性(如平台与常规库)来覆盖组件的默认选择。

此技术对于管理复杂构建中的平台和库依赖项非常有用,尤其是在您想使用常规库但平台依赖项声明不正确时

lib/build.gradle.kts
dependencies {
    // This is a platform dependency but you want the library
    implementation(platform("com.google.guava:guava:28.2-jre"))
}
lib/build.gradle
dependencies {
    // This is a platform dependency but you want the library
    implementation platform('com.google.guava:guava:28.2-jre')
}

在此示例中,替换规则以 com.google.guava:guava 的平台版本为目标,并将其替换为常规库版本

consumer/build.gradle.kts
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute(platform(module("com.google.guava:guava:28.2-jre")))
            .using(module("com.google.guava:guava:28.2-jre"))
    }
}
consumer/build.gradle
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute(platform(module('com.google.guava:guava:28.2-jre'))).
            using module('com.google.guava:guava:28.2-jre')
    }
}

如果没有 platform 关键字,则替换不会专门针对平台依赖项。

以下规则执行相同的替换,但使用更细粒度的变体表示法,从而可以自定义依赖项的属性

consumer/build.gradle.kts
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"))
    }
}
consumer/build.gradle
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 将自动匹配请求的属性。换句话说,如果您包含另一个构建,则隐式地意味着您正在用包含构建中等效的变体替换被替换模块的所有变体

用具有能力的依赖项替换依赖项

您可以用包含特定能力的不同变体替换依赖项。能力允许您指定依赖项的特定变体提供一组相关的功能或特性,例如测试 fixtures

此示例使用能力将常规依赖项替换为其测试 fixtures

build.gradle.kts
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")
            }
        })
    }
}
build.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')
            }
        }
    }
}

在这里,我们将常规的 com.acme:lib:1.0 依赖项替换为其 lib-test-fixtures 变体。requireCapability 函数指定新变体必须具有 com.acme:lib-test-fixtures 能力,从而确保为测试目的选择正确的依赖项版本。

替换规则中的能力用于精确匹配依赖项,并且 Gradle 仅替换与所需能力匹配的依赖项。

有关变体替换 API 的完整参考,请参阅 DependencySubstitutions API

用分类器或构件替换依赖项

您可以替换具有分类器的依赖项,或者用不具有分类器的依赖项替换具有分类器的依赖项,反之亦然。分类器通常用于表示同一构件的不同版本,例如特定于平台的构建或具有不同 API 的依赖项。虽然 Gradle 不鼓励使用分类器,但它提供了一种处理仍然使用分类器的情况下的替换的方法。

考虑以下设置

consumer/build.gradle.kts
dependencies {
    implementation("com.google.guava:guava:28.2-jre")
    implementation("co.paralleluniverse:quasar-core:0.8.0")
    implementation(project(":lib"))
}
consumer/build.gradle
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

lib/build.gradle.kts
dependencies {
    implementation("co.paralleluniverse:quasar-core:0.7.10:jdk8")
}
lib/build.gradle
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 依赖项时忽略分类器

consumer/build.gradle.kts
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute(module("co.paralleluniverse:quasar-core"))
            .using(module("co.paralleluniverse:quasar-core:0.8.0"))
            .withoutClassifier()
    }
}
consumer/build.gradle
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute module('co.paralleluniverse:quasar-core') using module('co.paralleluniverse:quasar-core:0.8.0') withoutClassifier()
    }
}

此规则有效地将图中找到的任何对 quasar-core 的依赖项替换为不带分类器的依赖项。

如果您需要替换为特定的分类器或构件,则可以在替换规则中指定分类器或构件详细信息。

有关更多详细信息,请参阅

4. 组件选择规则

当有多个版本可用且与版本选择器匹配时,组件选择规则可能会影响应选择哪个组件实例。规则应用于每个可用版本,并允许显式拒绝该版本。

这允许 Gradle 忽略任何不满足规则设置条件的组件实例。示例包括

  • 对于像 1.+ 这样的动态版本,某些版本可能会被显式拒绝选择。

  • 对于像 1.4 这样的静态版本,可以根据额外的组件元数据(例如 Ivy 分支属性)拒绝实例,从而允许使用来自后续仓库的实例。

规则通过 ComponentSelectionRules 对象配置。配置的每个规则都将使用 ComponentSelection 对象作为参数调用,该对象包含有关正在考虑的候选版本的信息。调用 ComponentSelection.reject(java.lang.String) 会导致显式拒绝给定的候选版本,在这种情况下,该候选版本将不被视为选择器的候选版本。

以下示例显示了一个规则,该规则不允许特定版本的模块,但允许动态版本选择下一个最佳候选版本

build.gradle.kts
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.+")
}
build.gradle
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 的形式指定

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 参数作为参数。

5. 默认依赖

您可以为配置设置默认依赖项,以确保在未指定显式依赖项时使用默认版本。

这对于依赖于版本化工​​具并且希望在用户未指定版本时提供默认值的插件非常有用

build.gradle.kts
configurations {
    create("pluginTool") {
        defaultDependencies {
            add(project.dependencies.create("org.gradle:my-util:1.0"))
        }
    }
}
build.gradle
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) 方法。

此方法将自动从配置中声明的所有依赖项中排除指定的传递依赖项

build.gradle.kts
configurations {
    "implementation" {
        exclude(group = "commons-collections", module = "commons-collections")
    }
}

dependencies {
    implementation("commons-beanutils:commons-beanutils:1.9.4")
    implementation("com.opencsv:opencsv:4.6")
}
build.gradle
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()

当找到同一依赖项的冲突版本时,这将使构建失败

build.gradle.kts
configurations.all {
    resolutionStrategy {
        failOnVersionConflict()
    }
}
build.gradle
configurations.all {
    resolutionStrategy {
        failOnVersionConflict()
    }
}

8. 禁用传递依赖

默认情况下,Gradle 解析给定模块的所有传递依赖项。

但是,在某些情况下,您可能希望禁用此行为,例如当您需要更多地控制依赖项或依赖项元数据不正确时。

您可以通过将 ModuleDependency.setTransitive(boolean) 设置为 false 来告诉 Gradle 禁用依赖项的传递依赖管理。

在以下示例中,为 guava 依赖项禁用了传递依赖解析

build.gradle.kts
dependencies {
    implementation("com.google.guava:guava:23.0") {
        isTransitive = false
    }
}
build.gradle
dependencies {
    implementation('com.google.guava:guava:23.0') {
        transitive = false
    }
}

这确保仅解析 guava 的主要构件,并且不会包含其任何传递依赖项。

禁用传递依赖项解析可能会要求您在构建脚本中声明必要的运行时依赖项,否则这些依赖项将自动解析。不这样做可能会导致运行时类路径问题。

如果您想全局禁用所有依赖项的传递解析,则可以在配置级别设置此行为

build.gradle.kts
configurations.all {
    isTransitive = false
}

dependencies {
    implementation("com.google.guava:guava:23.0")
}
build.gradle
configurations.all {
    transitive = false
}
dependencies {
    implementation 'com.google.guava:guava:23.0'
}

这将禁用项目中所有依赖项的传递解析。请注意,这可能要求您手动声明运行时所需的任何传递依赖项。

有关更多信息,请参阅 Configuration.setTransitive(boolean)

9. 依赖解析规则和其他条件

依赖解析规则在解析每个依赖项时执行,从而提供强大的 API 来修改依赖项的属性(例如 group、name 或 version),然后再最终确定解析。

这允许对依赖解析进行高级控制,使您可以在解析过程中将一个模块替换为另一个模块。

此功能对于实现高级依赖管理模式特别有用。使用依赖解析规则,您可以将依赖项重定向到特定版本,甚至完全不同的模块,从而使您可以跨项目强制执行一致的版本或覆盖有问题的依赖项

build.gradle.kts
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")
            }
        }
    }
}
build.gradle
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 构建中的模块版本在外部维护和审核。依赖解析规则提供了一种有效的方法来实现此目的

  • 开发人员在构建脚本中使用模块的 group 和 name 声明依赖项,但指定一个占位符版本,例如 default

  • 然后,依赖解析规则将 default 版本解析为批准的版本,该版本从已批准模块的公司目录中检索。

这种方法确保仅使用批准的版本,同时允许开发人员使用简化且一致的版本控制方案。

规则实现可以封装在公司插件中,从而使其易于在组织内的所有项目中应用

build.gradle.kts
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")
}
build.gradle
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,而其他版本不受影响。

build.gradle.kts
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")
        }
    }
}
build.gradle
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 作为最新版本。

与富版本约束的区别: 使用 富版本 约束,您可以完全拒绝某些版本,从而导致构建失败,或者在使用动态依赖项时选择未拒绝的版本。相比之下,像此处显示的依赖解析规则会操作正在请求的版本,当找到被拒绝的版本时,将其替换为已知的良好版本。这种方法是处理被拒绝版本的解决方案,而富版本约束是表达避免某些版本的意图。

延迟影响已解析的依赖项

插件可以通过在用户未指定版本时有条件地添加依赖项或设置首选版本来延迟影响依赖项。

以下是说明这些用例的两个示例。

此示例演示了如何根据某些条件(延迟评估)向配置添加依赖项。

build.gradle.kts
configurations {
    implementation {
        dependencies.addLater(project.provider {
            val dependencyNotation = conditionalLogic()
            if (dependencyNotation != null) {
                project.dependencies.create(dependencyNotation)
            } else {
                null
            }
        })
    }
}
build.gradle
configurations {
    implementation {
        dependencies.addLater(project.provider {
            def dependencyNotation = conditionalLogic()
            if (dependencyNotation != null) {
                return project.dependencies.create(dependencyNotation)
            } else {
                return null
            }
        })
    }
}

在本例中,addLater 用于延迟依赖项的评估,使其仅在满足某些条件时才添加。

在此示例中,构建脚本设置了依赖项的首选版本,如果未明确指定版本,则将使用该版本。

示例 2:首选依赖项的默认版本

build.gradle.kts
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")
            }
        }
    }
}
build.gradle
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,除非用户指定了另一个版本。