依赖版本对齐确保属于同一逻辑组(一个平台)的不同模块在依赖图中统一使用相同版本。

为什么会出现模块版本不一致?

Gradle支持对齐属于同一平台的模块版本。例如,组件的API和实现模块应该使用相同版本

然而,由于传递性依赖解析,同一平台内的模块最终可能使用不同版本,从而导致潜在的兼容性问题。

考虑以下示例,您的项目同时依赖于jackson-databindvert.x

build.gradle.kts
dependencies {
    implementation("com.fasterxml.jackson.core:jackson-databind:2.8.9")
    implementation("io.vertx:vertx-core:3.5.3")
}
build.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")
        }
    }
}

依赖解析可能导致

  • jackson-core2.9.5vertx-core所需)

  • jackson-databind2.9.5(通过冲突解析)

  • jackson-annotations2.9.0jackson-databind:2.9.5的依赖)

问题在于Vert.x3.5.0)使用了旧版本的Jackson2.9.0),但显式依赖(2.9.5)强制Gradle将Vert.xJackson依赖从2.9.0升级到2.9.5

dependencies.out
> 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)可用,则将其作为平台导入

build.gradle.kts
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")
}
build.gradle

运行./gradlew dependencies --configuration runtimeClasspath将显示对齐的依赖项

dependencies-bom.out
> 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会根据正在使用的模块动态构建平台。为此,您必须定义组件元数据规则

build.gradle.kts
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>()
    }
}
build.gradle

这确保了所有Jackson模块对齐到相同版本,即使是传递性引入的。

运行./gradlew dependencies --configuration runtimeClasspath将显示对齐的依赖项

dependencies-platform.out
> 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平台插件

当项目有多个版本同步的模块(例如,libutilscore)时,使用混合版本(例如,core:1.0lib:1.1)可能导致运行时问题或不兼容性。

考虑一个具有三个模块项目

  • lib

  • utils

  • core (依赖于libutils

消费者项目声明

  • core 版本1.0

  • lib 版本1.1

默认情况下,Gradle选择core:1.0lib:1.1,导致版本不对齐

要解决此问题,引入一个强制约束的平台模块

build.gradle.kts
plugins {
    `java-platform`
}

dependencies {
    // The platform declares constraints on all components that
    // require alignment
    constraints {
        api(project(":core"))
        api(project(":lib"))
        api(project(":utils"))
    }
}
build.gradle
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"))
    }
}

每个模块都应声明对平台的依赖

build.gradle.kts
dependencies {
    // Each project has a dependency on the platform
    api(platform(project(":platform")))

    // And any additional dependency required
    implementation(project(":lib"))
    implementation(project(":utils"))
}
build.gradle
dependencies {
    // Each project has a dependency on the platform
    api(platform(project(":platform")))

    // And any additional dependency required
    implementation(project(":lib"))
    implementation(project(":utils"))
}

这确保所有依赖(corelibutils)都能一致地解析到版本1.1

总结

通过使用平台和BOM,Gradle可确保一致的依赖版本,避免兼容性问题。当不存在已发布的BOM时,虚拟平台允许手动对齐依赖。