在许多情况下,您想使用特定模块依赖项的最新版本或一系列版本中的最新版本. 在开发过程中可能会要求这样做,或者您可能正在开发一个旨在与一系列依赖版本一起使用的库. 您可以使用动态版本轻松地依赖这些不断变化的依赖关系. 动态版本可以是版本范围(例如2.+ ),也可以是可用的最新版本的占位符,例如latest.integration .

另外,您请求的模块甚至可以针对同一版本(即所谓的" 更改版本")随时间变化 . 此类更改模块的一个示例是Maven SNAPSHOT模块,该模块始终指向最新发布的工件. 换句话说,标准的Maven快照是一个不断发展的模块,它是一个"不断变化的模块".

Using dynamic versions and changing modules can lead to unreproducible builds. As new versions of a particular module are published, its API may become incompatible with your source code. Use this feature with caution!

Declaring a dynamic version

项目可能会采用更激进的方法来消耗对模块的依赖. 例如,您可能希望始终集成最新版本的依赖项,以便在任何给定时间使用最先进的功能. 动态版本允许解析给定模块的最新版本或版本范围的最新版本.

在构建中使用动态版本会承担潜在破坏它的风险. 一旦发布了包含不兼容API更改的依赖关系的新版本,您的源代码就可能停止编译.

例子1.声明一个动态版本的依赖
build.gradle
plugins {
    id 'java-library'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework:spring-web:5.+'
}
build.gradle.kts
plugins {
    `java-library`
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework:spring-web:5.+")
}

构建扫描可以有效地可视化动态依赖项版本及其各自的选定版本.

dependency management dynamic dependency build scan
图1.构建扫描中的动态依赖关系

默认情况下,Gradle将动态版本的依赖项缓存24小时. 在此时间范围内,Gradle不会尝试从声明的存储库中解析较新的版本. 可以根据需要配置阈值 ,例如,如果您想更早地解析新版本.

Declaring a changing version

在发布应用程序或库的新版本之前,团队可能会决定实施一系列功能. 允许消费者尽早集成其成品的未完成版本的常见策略通常是发布具有所谓更改版本的模块. 版本更改表示该功能集仍在积极开发中,尚未发布稳定的版本以实现普遍可用性.

In Maven repositories, changing versions are commonly referred to as snapshot versions. Snapshot versions contain the suffix -SNAPSHOT. The following example demonstrates how to declare a snapshot version on the Spring dependency.

例子2.声明一个版本变化的依赖
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'
}
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")
}

默认情况下,Gradle将更改的依赖项版本缓存24小时. 在此时间范围内,Gradle不会尝试从声明的存储库中解析较新的版本. 可以根据需要配置阈值 ,例如,如果您想更早地解析新的快照版本.

Gradle足够灵活,可以将任何版本视为更改版本,例如,如果您想为Ivy模块的快照行为建模. 您需要做的就是将属性ExternalModuleDependency.setChanging(boolean)设置true .

Controlling dynamic version caching

默认情况下,Gradle会在24小时内缓存动态版本和更改模块. 在这段时间内,Gradle不会与任何已声明的远程存储库联系以获取新版本. 如果您希望Gradle更频繁地检查远程存储库或在每次执行构建时检查远程存储库,则需要更改生存时间(TTL)阈值.

由于HTTP(s)调用的数量增加,因此对于动态版本或更改版本使用较短的TTL阈值可能会导致构建时间更长.

您可以使用命令行选项覆盖默认的缓存模式. 您还可以使用解析策略以编程方式更改构建中的缓存过期时间 .

Controlling dependency caching programmatically

您可以使用ResolutionStrategy对配置进行编程来微调缓存的某些方面. 如果您想永久更改设置,则编程方式非常有用.

默认情况下,Gradle将动态版本缓存24小时. 要更改Gradle将解析后的版本缓存为动态版本的时间,请使用:

例子3.动态版本缓存控制
build.gradle
configurations.all {
    resolutionStrategy.cacheDynamicVersionsFor 10, 'minutes'
}
build.gradle.kts
configurations.all {
    resolutionStrategy.cacheDynamicVersionsFor(10, "minutes")
}

默认情况下,Gradle将更改的模块缓存24小时. 要更改Gradle将为更改的模块缓存元数据和工件的时间,请使用:

例子4.改变模块缓存控制
build.gradle
configurations.all {
    resolutionStrategy.cacheChangingModulesFor 4, 'hours'
}
build.gradle.kts
configurations.all {
    resolutionStrategy.cacheChangingModulesFor(4, "hours")
}

Controlling dependency caching from the command line

Avoiding network access with offline mode

--offline命令行开关告诉Gradle始终使用缓存中的依赖项模块,无论是否要再次检查它们. 当离线运行时,Gradle将永远不会尝试访问网络来执行依赖关系解析. 如果依赖项缓存中不存在所需的模块,则生成执行将失败.

Refreshing dependencies

您可以控制依赖项缓存的行为,以从命令行进行不同的构建调用. 命令行选项有助于对构建的一次执行做出选择性的临时选择.

有时,Gradle Dependency Cache可能与已配置存储库的实际状态不同步. 最初可能是错误配置了存储库,或者错误地发布了"不变"模块. 要刷新依赖项缓存中的所有依赖项,请在命令行上使用--refresh-dependencies选项.

--refresh-dependencies选项告诉Gradle忽略所有已缓存模块和工件的缓存条目. 将对所有已配置的存储库执行全新的解析,并重新计算动态版本,刷新模块并下载工件. 但是,在可能的情况下,Gradle将在再次下载之前检查先前下载的工件是否有效. 通过将存储库中已发布的SHA1值与现有下载的工件的SHA1值进行比较来完成此操作.

  • 动态依赖的新版本

  • 更改模块的新版本(使用相同版本字符串但可以具有不同内容的模块)

刷新依赖关系将导致Gradle使其列表缓存无效. 然而:

  • 它将对元数据文件执行HTTP HEAD请求,但如果它们相同则不会重新下载它们

  • 它将对工件文件执行HTTP HEAD请求,但如果它们相同则不会重新下载它们

换句话说,刷新依赖关系在您实际使用动态依赖关系您具有未知的变化依赖关系时才有影响(在这种情况下,您有责任正确地将其声明为Gradle作为变化依赖关系).

认为使用--refresh-dependencies将强制下载依赖项是一个常见的误解. 事实并非如此:Gradle将仅执行刷新动态依赖项严格要求的操作. 这可能涉及下载新的清单或元数据文件,甚至是工件,但如果不进行任何更改,则影响最小.

Using component selection rules

当有多个与版本选择器匹配的版本可用时,组件选择规则可能会影响应该选择哪个组件实例. 规则适用于每个可用版本,并允许该规则明确拒绝该版本. 这使Gradle可以忽略不满足规则设置的条件的任何组件实例. 示例包括:

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

  • 对于像1.4这样的静态版本,可以根据其他组件元数据(例如Ivy分支属性)拒绝实例,从而允许使用后续存储库中的实例.

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

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

例子5.组件选择规则
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.+"
}
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.+")
}

请注意,首先从最高版本开始应用版本选择. 选择的版本将是所有组件选择规则都接受的第一个版本. 如果没有任何规则明确拒绝该版本,则认为该版本已被接受.

同样,规则可以针对特定模块. 必须以group:module的形式指定group:module .

例子6.具有模块目标的组件选择规则
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'")
                    }
                }
            }
        }
    }
}
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'")
                    }
                }
            }
        }
    }
}

组件选择规则在选择版本时也可以考虑组件元数据. 可以考虑的其他可能的元数据是ComponentMetadataIvyModuleDescriptor . 请注意,此额外信息可能并不总是可用,因此应检查是否为null值.

例子7.带有元数据的组件选择规则
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 have testing branch or milestone status")
                    }
                }
            }
        }
    }
}
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")
                    }
                }
            }
        }
    }
}

请注意,在声明组件选择规则时, 始终需要ComponentSelection参数作为参数.