依赖版本对齐允许属于同一逻辑组(平台)的不同模块在依赖图中具有相同的版本。

处理不一致的模块版本

Gradle 支持对齐属于同一“平台”的模块版本。例如,组件的 API 和实现模块通常最好使用相同的版本。然而,由于传递依赖解析的游戏,属于同一平台的不同模块最终可能使用不同的版本。例如,您的项目可能依赖于jackson-databindvert.x库,如下所示:

build.gradle.kts
dependencies {
    // a dependency on Jackson Databind
    implementation("com.fasterxml.jackson.core:jackson-databind:2.8.9")

    // and a dependency on vert.x
    implementation("io.vertx:vertx-core:3.5.3")
}
build.gradle
dependencies {
    // a dependency on Jackson Databind
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.8.9'

    // and a dependency on vert.x
    implementation 'io.vertx:vertx-core:3.5.3'
}

因为vert.x依赖于jackson-core,我们实际上会解析以下依赖版本:

  • jackson-core版本2.9.5(由 带来vertx-core

  • jackson-databind版本2.9.5(通过冲突解决)

  • jackson-annotation版本2.9.0( 的依赖项jackson-databind:2.9.5

很容易最终得到一组不能很好地协同工作的版本。为了解决这个问题,Gradle 支持依赖版本对齐,这是由平台概念支持的。平台代表一组“协同工作”的模块。要么是因为它们实际上是作为一个整体发布的(当平台的一个成员发布时,所有其他模块也以相同的版本发布),要么是因为有人测试了这些模块并表明它们可以很好地协同工作(通常,春季平台)。

使用 Gradle 本地调整版本

Gradle 本身支持 Gradle 生成的模块的对齐。这是依赖约束传递性的直接结果。因此,如果您有一个多项目构建,并且希望消费者获得所有模块的相同版本,Gradle 提供了一种使用Java Platform Plugin来实现此目的的简单方法。

例如,如果您有一个由 3 个模块组成的项目:

  • lib

  • utils

  • core,取决于libutils

以及声明以下依赖项的消费者:

  • core1.0版

  • lib1.1版

那么默认情况下解析将选择core:1.0lib:1.1,因为lib不依赖于core。我们可以通过在项目中添加一个新模块来解决这个问题,即一个平台,它将对项目的所有模块添加约束:

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"))
}

重要的是,平台包含对所有组件的约束,而且每个组件都对平台具有依赖性。通过这样做,每当 Gradle 在图表上添加对平台模块的依赖项时,它还将包括对平台其他模块的约束。这意味着,如果我们看到属于同一平台的另一个模块,我们将自动升级到同一版本。

在我们的示例中,这意味着我们首先看到,它带来了一个对和进行约束的core:1.0平台。然后我们添加它依赖于.通过冲突解决,我们选择平台,该平台对.然后我们解决和之间的冲突,这意味着和现在正确对齐。1.0lib:1.0lib:1.0lib:1.1platform:1.11.1core:1.1core:1.0core:1.1corelib

仅当您使用 Gradle 模块元数据时,才会对已发布的组件强制执行此行为。

对齐未使用 Gradle 发布的模块版本

每当发布者不使用 Gradle 时(就像我们的 Jackson 示例一样),我们可以向 Gradle 解释所有 Jackson 模块“属于”同一平台,并受益于与本机对齐相同的行为。有两种选项可以表达一组模块属于一个平台:

  1. 平台作为BOM发布并可以使用:例如,可以用作平台。在这种情况下,Gradle 缺少的信息是,如果使用平台的成员之一,则应将平台添加到依赖项中。com.fasterxml.jackson:jackson-bom

  2. 无法使用现有平台。相反,一个虚拟平台应该由 Gradle 创建:在这种情况下,Gradle 基于所有使用的成员构建平台本身。

要向 Gradle 提供缺失的信息,您可以定义组件元数据规则,如下所述。

使用已发布的 BOM 调整模块的版本

build.gradle.kts
abstract class JacksonBomAlignmentRule: ComponentMetadataRule {
    override fun execute(ctx: ComponentMetadataContext) {
        ctx.details.run {
            if (id.group.startsWith("com.fasterxml.jackson")) {
                // declare that Jackson modules belong to the platform defined by the Jackson BOM
                belongsTo("com.fasterxml.jackson:jackson-bom:${id.version}", false)
            }
        }
    }
}
build.gradle
abstract class JacksonBomAlignmentRule implements ComponentMetadataRule {
    void execute(ComponentMetadataContext ctx) {
        ctx.details.with {
            if (id.group.startsWith("com.fasterxml.jackson")) {
                // declare that Jackson modules belong to the platform defined by the Jackson BOM
                belongsTo("com.fasterxml.jackson:jackson-bom:${id.version}", false)
            }
        }
    }
}

通过使用belongsTowith false不是虚拟的),我们声明所有模块都属于同一个发布平台。在这种情况下,平台是com.fasterxml.jackson:jackson-bom,Gradle 将在声明的存储库中查找它,就像任何其他模块一样。

build.gradle.kts
dependencies {
    components.all<JacksonBomAlignmentRule>()
}
build.gradle
dependencies {
    components.all(JacksonBomAlignmentRule)
}

使用该规则,上例中的版本将与所选的定义版本对齐com.fasterxml.jackson:jackson-bom。在这种情况下,com.fasterxml.jackson:jackson-bom:2.9.5将选择2.9.5所选模块的最高版本。在该 BOM 中,定义并将使用以下版本: jackson-core:2.9.5jackson-databind:2.9.5jackson-annotation:2.9.0。此处的较低版本jackson-annotation可能是所需的结果,因为这是 BOM 推荐的结果。

从 Gradle 6.1 开始,这种行为就可靠了。实际上,它类似于组件元数据规则,该规则将平台依赖项添加到使用withDependencies.

在没有发布平台的情况下调整模块版本

build.gradle.kts
abstract class JacksonAlignmentRule: ComponentMetadataRule {
    override fun execute(ctx: ComponentMetadataContext) {
        ctx.details.run {
            if (id.group.startsWith("com.fasterxml.jackson")) {
                // declare that Jackson modules all belong to the Jackson virtual platform
                belongsTo("com.fasterxml.jackson:jackson-virtual-platform:${id.version}")
            }
        }
    }
}
build.gradle
abstract class JacksonAlignmentRule implements ComponentMetadataRule {
    void execute(ComponentMetadataContext ctx) {
        ctx.details.with {
            if (id.group.startsWith("com.fasterxml.jackson")) {
                // declare that Jackson modules all belong to the Jackson virtual platform
                belongsTo("com.fasterxml.jackson:jackson-virtual-platform:${id.version}")
            }
        }
    }
}

通过使用belongsTo不带进一步参数的关键字(platform is virtual),我们声明所有模块都属于同一个虚拟平台,该平台受到引擎的特殊对待。不会从存储库中检索虚拟平台。在这种情况下,标识符com.fasterxml.jackson:jackson-virtual-platform是您作为构建作者自己定义的。然后,Gradle 通过收集belongsTo指向同一虚拟平台的所有语句来即时创建平台的“内容” 。

build.gradle.kts
dependencies {
    components.all<JacksonAlignmentRule>()
}
build.gradle
dependencies {
    components.all(JacksonAlignmentRule)
}

使用该规则,上例中的所有版本都将与2.9.5.在这种情况下,也jackson-annotation:2.9.5将被采用,因为这就是我们定义本地虚拟平台的方式。

对于发布平台和虚拟平台,Gradle 允许您通过指定对平台的强制依赖来覆盖平台本身的版本选择:

build.gradle.kts
dependencies {
    // Forcefully downgrade the virtual Jackson platform to 2.8.9
    implementation(enforcedPlatform("com.fasterxml.jackson:jackson-virtual-platform:2.8.9"))
}
build.gradle
dependencies {
    // Forcefully downgrade the virtual Jackson platform to 2.8.9
    implementation enforcedPlatform('com.fasterxml.jackson:jackson-virtual-platform:2.8.9')
}