本指南解释了如何防止由于 Gradle 的激进依赖解析而导致的意外依赖升级。
为什么要防止意外的依赖升级?
在 Gradle 中管理依赖时,您可能会遇到传递性依赖导致依赖意外升级到更新版本的情况。这可能导致意外行为或兼容性问题。Gradle 默认执行乐观升级,这意味着当在依赖图中发现同一个依赖存在多个版本时,它会解析到可用的最高版本。
例如,如果 commons-lang3
的 3.1
和 3.2
版本同时存在,即使您的构建脚本显式声明了 3.1
,Gradle 也会选择 3.2
。如果您的构建使用了 3.1
版本中存在但 3.2
版本中没有的功能,或者您尚未更新构建以兼容 3.2
版本,您可能不希望 Gradle 的依赖解析过程进行这种激进的升级。
dependencies {
implementation("org.apache.commons:commons-lang3:3.2")
constraints {
implementation("org.apache.commons:commons-lang3:3.1") {
because("Version 1.3 introduces breaking changes not yet handled")
}
}
}
dependencies {
implementation("org.apache.commons:commons-lang3:3.2")
constraints {
implementation("org.apache.commons:commons-lang3:3.1") {
because("Version 1.3 introduces breaking changes not yet handled")
}
}
}
运行 ./gradlew dependencies --configuration runtimeClasspath
会展示结果
> Task :dependencies
------------------------------------------------------------
Root project 'how_to_prevent_accidental_upgrades'
------------------------------------------------------------
runtimeClasspath - Compile classpath for source set 'main'.
+--- org.apache.commons:commons-lang3:3.2
\--- org.apache.commons:commons-lang3:3.1 -> 3.2 (c)
选项 1:强制执行严格的依赖解析
Gradle 提供了一个选项,可以在发生版本冲突时使构建失败,从而确保不会发生意外升级。
要启用此功能,请将您的构建配置为在版本冲突时失败
configurations.all {
resolutionStrategy.failOnVersionConflict()
}
configurations.all {
resolutionStrategy.failOnVersionConflict()
}
启用此设置后,如果一个依赖存在多个版本冲突,Gradle 将停止执行并报告错误,而不是自动升级到最高版本。
运行 ./gradlew dependencies --configuration runtimeClasspath
会展示失败情况
> Task :dependencies FAILED
------------------------------------------------------------
Root project 'how_to_prevent_accidental_upgrades'
------------------------------------------------------------
runtimeClasspath - Runtime classpath of source set 'main'.
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':dependencies'.
> Could not resolve all dependencies for configuration ':runtimeClasspath'.
> Conflict found for the following module:
- org.apache.commons:commons-lang3 between versions 3.2 and 3.1
选项 2:使用依赖约束
如果您有多个依赖项使用了共享库,您可以使用 依赖约束 在所有模块中强制使用一致的版本。
dependencies {
implementation("org.apache.commons:commons-lang3")
constraints {
implementation("org.apache.commons:commons-lang3") {
version {
strictly("3.1")
}
}
}
}
dependencies {
implementation("org.apache.commons:commons-lang3")
constraints {
implementation("org.apache.commons:commons-lang3") {
version {
strictly("3.1")
}
}
}
}
使用 strictly("3.1")
,Gradle 确保没有其他版本可以覆盖指定的依赖。
运行 ./gradlew dependencies --configuration runtimeClasspath
会展示结果
> Task :dependencies
------------------------------------------------------------
Root project 'how_to_prevent_accidental_upgrades'
------------------------------------------------------------
runtimeClasspath - Runtime classpath of source set 'main'.
+--- org.apache.commons:commons-lang3 -> 3.1
\--- org.apache.commons:commons-lang3:{strictly 3.1} -> 3.1 (c)
选项 3:将依赖锁定到特定版本
为了更稳健的解决方案,您可以使用 依赖锁定 来确保 Gradle 在不同构建中一致地解析到相同的依赖版本。
要启用依赖锁定
configurations.all {
resolutionStrategy.activateDependencyLocking()
}
configurations.all {
resolutionStrategy.activateDependencyLocking()
}
然后,生成一个锁定文件
./gradlew dependencies --write-locks
这会创建一个 gradle.lockfile
文件,其中存储了精确的依赖版本,除非显式更新,否则可防止未来的升级。
总结
Gradle 的乐观依赖解析可能会意外升级依赖,导致兼容性问题。
为了防止意外升级,您可以
-
强制严格解析 (
failOnVersionConflict
) -
使用
strictly
关键字显式约束依赖 -
使用依赖锁定 (
activateDependencyLocking
) 以在不同构建中保持版本一致。