依赖版本对齐确保属于同一逻辑组(一个 平台)的不同模块在依赖图中使版本一致。
为什么会出现模块版本不一致?
Gradle 支持对齐属于同一平台的模块版本。例如,组件的 API 和实现模块应该使用相同的版本。
然而,由于传递性依赖解析,同一平台内的模块最终可能会使用不同的版本,从而导致潜在的兼容性问题。
考虑以下示例,你的项目同时依赖于 jackson-databind
和 vert.x
dependencies {
implementation("com.fasterxml.jackson.core:jackson-databind:2.8.9")
implementation("io.vertx:vertx-core:3.5.3")
}
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")
}
}
}
依赖解析可能导致
-
jackson-core
→2.9.5
(vertx-core 所需) -
jackson-databind
→2.9.5
(通过冲突解决得到) -
jackson-annotations
→2.9.0
(jackson-databind:2.9.5 的依赖)
问题在于 Vert.x
(3.5.0
) 使用的是旧版本 Jackson
(2.9.0
),但显式依赖 (2.9.5
) 强制 Gradle 将 Vert.x
的 Jackson
依赖从 2.9.0
升级到 2.9.5
> Task :dependencies
------------------------------------------------------------
Root project 'how_to_align_dependency_versions'
------------------------------------------------------------
runtimeClasspath - Runtime classpath of source set 'main'.
+--- com.fasterxml.jackson.core:jackson-databind:2.8.9 -> 2.9.5
| +--- com.fasterxml.jackson.core:jackson-annotations:2.9.0
| \--- com.fasterxml.jackson.core:jackson-core:2.9.5
\--- io.vertx:vertx-core:3.5.3
+--- io.netty:netty-common:4.1.19.Final
...
| \--- io.netty:netty-transport:4.1.19.Final (*)
+--- com.fasterxml.jackson.core:jackson-core:2.9.5
\--- com.fasterxml.jackson.core:jackson-databind:2.9.5 (*)
这种不匹配可能导致兼容性问题和意外失败。
Gradle 通过平台提供依赖版本对齐,确保相关模块使用一致的版本。
选项 1:使用已发布的平台
如果存在公共平台(也称为 BOM),可以将其导入为平台
dependencies {
implementation(platform("com.fasterxml.jackson:jackson-bom:2.8.9"))
implementation("com.fasterxml.jackson.core:jackson-databind:2.8.9")
implementation("io.vertx:vertx-core:3.5.3")
}
运行 ./gradlew dependencies --configuration runtimeClasspath
可以展示对齐后的依赖
> Task :dependencies
------------------------------------------------------------
Root project 'how_to_align_dependency_versions'
------------------------------------------------------------
runtimeClasspath - Runtime classpath of source set 'main'.
+--- com.fasterxml.jackson:jackson-bom:2.8.9
| +--- com.fasterxml.jackson.core:jackson-databind:2.8.9 -> 2.9.5 (c)
| +--- com.fasterxml.jackson.core:jackson-core:2.8.9 -> 2.9.5 (c)
| \--- com.fasterxml.jackson.core:jackson-annotations:2.8.0 -> 2.9.0 (c)
+--- com.fasterxml.jackson.core:jackson-databind:2.8.9 -> 2.9.5
| +--- com.fasterxml.jackson.core:jackson-annotations:2.9.0
| \--- com.fasterxml.jackson.core:jackson-core:2.9.5
\--- io.vertx:vertx-core:3.5.3
+--- io.netty:netty-common:4.1.19.Final
...
| \--- io.netty:netty-transport:4.1.19.Final (*)
+--- com.fasterxml.jackson.core:jackson-core:2.9.5
\--- com.fasterxml.jackson.core:jackson-databind:2.9.5 (*)
选项 2:创建虚拟平台
如果不存在公共 BOM,你可以创建一个虚拟平台。
在这种情况下,Gradle 会根据正在使用的模块动态地构建平台。为此,你必须定义组件元数据规则
abstract class JacksonAlignmentRule : ComponentMetadataRule {
override fun execute(ctx: ComponentMetadataContext) {
ctx.details.run {
if (id.group.startsWith("com.fasterxml.jackson")) {
belongsTo("com.fasterxml.jackson:jackson-virtual-platform:${id.version}")
}
}
}
}
dependencies {
implementation("com.fasterxml.jackson.core:jackson-databind:2.8.9")
implementation("io.vertx:vertx-core:3.5.3")
dependencies {
components.all<JacksonAlignmentRule>()
}
}
这确保所有 Jackson 模块对齐到同一版本,即使是传递性引入的。
运行 ./gradlew dependencies --configuration runtimeClasspath
可以展示对齐后的依赖
> Task :dependencies
------------------------------------------------------------
Root project 'how_to_align_dependency_versions'
------------------------------------------------------------
runtimeClasspath - Runtime classpath of source set 'main'.
+--- com.fasterxml.jackson.core:jackson-databind:2.8.9 -> 2.9.5
| +--- com.fasterxml.jackson.core:jackson-annotations:2.9.0 -> 2.9.5
| \--- com.fasterxml.jackson.core:jackson-core:2.9.5
\--- io.vertx:vertx-core:3.5.3
+--- io.netty:netty-common:4.1.19.Final
...
| \--- io.netty:netty-transport:4.1.19.Final (*)
+--- com.fasterxml.jackson.core:jackson-core:2.9.5
\--- com.fasterxml.jackson.core:jackson-databind:2.9.5 (*)
选项 3:使用 Java 插件
Gradle 原生支持版本对齐,通过使用Java 平台插件。
当项目有多个一起版本化的模块(例如 lib
、utils
、core
)时,使用混合版本(例如 core:1.0
和 lib:1.1
)可能导致运行时问题或不兼容。
考虑一个包含三个模块的项目
-
lib
-
utils
-
core
(依赖于lib
和utils
)
一个消费者项目声明
-
core
版本 1.0 -
lib
版本 1.1
默认情况下,Gradle 会选择 core:1.0
和 lib:1.1
,导致版本不对齐。
要解决此问题,引入一个强制约束的平台模块
plugins {
`java-platform`
}
dependencies {
// The platform declares constraints on all components that
// require alignment
constraints {
api(project(":core"))
api(project(":lib"))
api(project(":utils"))
}
}
plugins {
id 'java-platform'
}
dependencies {
// The platform declares constraints on all components that
// require alignment
constraints {
api(project(":core"))
api(project(":lib"))
api(project(":utils"))
}
}
每个模块都应声明对平台的依赖
dependencies {
// Each project has a dependency on the platform
api(platform(project(":platform")))
// And any additional dependency required
implementation(project(":lib"))
implementation(project(":utils"))
}
dependencies {
// Each project has a dependency on the platform
api(platform(project(":platform")))
// And any additional dependency required
implementation(project(":lib"))
implementation(project(":utils"))
}
这确保所有依赖(core
、lib
和 utils
)一致解析到版本 1.1
。
总结
使用平台和 BOM,Gradle 确保一致的依赖版本,避免兼容性问题。当没有已发布的 BOM 时,虚拟平台允许手动对齐依赖。