本指南解释了如何由于 Gradle 激进的依赖解析而**防止意外的依赖版本**。
为什么要防止意外的依赖升级?
在 Gradle 中管理依赖时,您可能会遇到传递性依赖导致**意外升级**到新版本的情况。这可能导致意外行为或兼容性问题。Gradle 默认执行*乐观升级*,这意味着当在依赖图中找到多个依赖版本时,它会解析到可用的最高版本。
例如,如果 `commons-lang3` 的 `3.1` 和 `3.2` 版本都存在,Gradle 将选择 `3.2`,即使您的构建脚本明确声明了 `3.1`。如果您的构建使用了版本 `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`) 以在构建之间保持版本一致。