在某些情况下,您可能希望完全控制依赖项图。特别是,您可能希望确保
-
构建脚本中声明的版本实际上与解析的版本相对应
-
或确保依赖项解析随着时间的推移是可重现的
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.+
等) -
或使用更改版本(SNAPSHOT、内容不断变化的固定版本等)
处理动态版本的推荐方法是使用依赖项锁定。但是,也可以完全阻止使用动态版本,这是一种替代策略
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 插件扩展文档。