在某些情况下,您可能希望完全控制依赖关系图。特别是,您可能需要确保:
-
构建脚本中声明的版本实际上对应于正在解析的版本
-
或者确保依赖性解析随着时间的推移是可重现的
Gradle 提供了通过配置解析策略来执行此操作的方法。
版本冲突失败
每当 Gradle 在依赖关系图中的两个不同版本中找到相同的模块时,就会出现版本冲突。默认情况下,Gradle 执行乐观升级,这意味着如果在图中找到版本1.1
和,我们将解析为最高版本。但是,由于传递依赖,很容易忽略某些依赖项的升级。在上面的示例中,如果构建脚本中使用了一个版本并且传递了一个版本,那么您可以在不实际注意到的情况下使用。1.3
1.3
1.1
1.3
1.3
为了确保您了解此类升级,Gradle 提供了一种可以在配置的解决策略中激活的模式。想象一下以下依赖关系声明:
dependencies {
implementation("org.apache.commons:commons-lang3:3.0")
// the following dependency brings lang3 3.8.1 transitively
implementation("com.opencsv:opencsv:4.6")
}
dependencies {
implementation 'org.apache.commons:commons-lang3:3.0'
// the following dependency brings lang3 3.8.1 transitively
implementation 'com.opencsv:opencsv:4.6'
}
然后默认情况下 Gradle 会升级,但构建commons-lang3
可能会失败:
configurations.all {
resolutionStrategy {
failOnVersionConflict()
}
}
configurations.all {
resolutionStrategy {
failOnVersionConflict()
}
}
确保分辨率可重现
在某些情况下,依赖关系解析可能会随着时间的推移而不稳定。也就是说,如果您在日期 D 构建,则在日期 D+x 构建可能会给出不同的解析结果。
在以下情况下可以这样做:
-
使用动态依赖版本(版本范围、、、
latest.release
...1.+
) -
或使用更改版本(快照、内容更改的固定版本……)
处理动态版本的推荐方法是使用依赖锁定。但是,可以完全阻止使用动态版本,这是一种替代策略:
configurations.all {
resolutionStrategy {
failOnDynamicVersions()
}
}
configurations.all {
resolutionStrategy {
failOnDynamicVersions()
}
}
同样,可以通过激活此标志来防止使用更改版本:
configurations.all {
resolutionStrategy {
failOnChangingVersions()
}
}
configurations.all {
resolutionStrategy {
failOnChangingVersions()
}
}
在发布时更改版本失败是一个很好的做法。
最终,可以使用单个调用将动态版本失败和版本更改结合起来:
configurations.all {
resolutionStrategy {
failOnNonReproducibleResolution()
}
}
configurations.all {
resolutionStrategy {
failOnNonReproducibleResolution()
}
}
获得一致的依赖解析结果
依赖解析一致性是一个正在孵化的功能 |
一个常见的误解是应用程序只有一个依赖图。事实上,Gradle 在构建过程中会解析许多不同的依赖关系图,即使在单个项目中也是如此。例如,编译时使用的依赖关系图与运行时使用的依赖关系图不同。一般来说,运行时的依赖关系图是编译依赖关系的超集(该规则也有例外,例如,某些依赖关系在运行时二进制文件中重新打包的情况)。
Gradle 独立解析这些依赖图。这意味着,例如在 Java 生态系统中,“编译类路径”的解析不会影响“运行时类路径”的解析。同样,测试依赖项最终可能会影响生产依赖项的版本,从而在执行测试时导致一些令人惊讶的结果。
通过启用依赖性解析一致性可以减轻这些令人惊讶的行为。
启用项目本地依赖解析一致性
例如,假设您的 Java 库依赖于以下库:
dependencies {
implementation("org.codehaus.groovy:groovy:3.0.1")
runtimeOnly("io.vertx:vertx-lang-groovy:3.9.4")
}
dependencies {
implementation 'org.codehaus.groovy:groovy:3.0.1'
runtimeOnly 'io.vertx:vertx-lang-groovy:3.9.4'
}
然后解析compileClasspath
配置会将groovy
库解析为预期的版本3.0.1
。但是,解析runtimeClasspath
配置将返回groovy 3.0.2
.
原因是 的传递依赖vertx
(即依赖runtimeOnly
)带来了更高版本的groovy
.一般来说,这不是问题,但这也意味着您将在运行时使用的 Groovy 库的版本将与您用于编译的版本不同。
为了避免这种情况,Gradle提供了一个API来解释配置应该一致地解析。
声明配置之间的分辨率一致性
在上面的示例中,我们可以通过声明“运行时类路径”应与“编译类路径”一致来声明我们希望在运行时获得与编译时相同版本的公共依赖项:
configurations {
runtimeClasspath.get().shouldResolveConsistentlyWith(compileClasspath.get())
}
configurations {
runtimeClasspath.shouldResolveConsistentlyWith(compileClasspath)
}
因此, 和 都runtimeClasspath
将compileClasspath
解析 Groovy 3.0.1。
这种关系是有向的,这意味着如果runtimeClasspath
必须解析配置,Gradle 将首先解析compileClasspath
,然后将解析结果作为严格约束“注入”到 中runtimeClasspath
。
如果由于某种原因,两个图的版本无法“对齐”,则解决方案将因号召性用语而失败。
在 Java 生态系统中声明一致的解决方案
runtimeClasspath
上面的例子compileClasspath
在 Java 生态系统中很常见。然而,仅声明这两种配置之间的一致性通常是不够的。例如,您很可能希望测试运行时类路径与运行时类路径一致
。
为了使这更容易,Gradle 提供了一种使用扩展为 Java 生态系统配置一致分辨率的方法java
:
java {
consistentResolution {
useCompileClasspathVersions()
}
}
java {
consistentResolution {
useCompileClasspathVersions()
}
}
请参阅Java 插件扩展文档以获取更多配置选项。