您可以指定具有精确版本或版本范围的依赖项,以定义您的项目可以使用哪些版本

dependencies {
    implementation("org.springframework:spring-core:5.3.8")
    implementation("org.springframework:spring-core:5.3.+")
    implementation("org.springframework:spring-core:latest.release")
    implementation("org.springframework:spring-core:[5.2.0, 5.3.8]")
    implementation("org.springframework:spring-core:[5.2.0,)")
}

理解版本声明

Gradle 支持多种声明版本和范围的方式

版本 示例 注意

精确版本

1.3, 1.3.0-beta3, 1.0-20150201.131010-1

一个特定版本。

Maven 风格范围

[1.0,), [1.1, 2.0), (1.2, 1.5]

[ ] 表示包含边界;( ) 表示排除边界。请参阅下方了解更多信息

当上限或下限缺失时,该范围没有上限或下限。

一个上限排除项作为前缀排除项。

前缀版本范围

1.+, 1.3.+

仅包含与 + 之前部分完全匹配的版本。

声明版本为 +,不带任何前缀,将包含任何版本。

latest-status 版本

latest.integration, latest.release

匹配具有指定状态的最高版本。请参阅 ComponentMetadata.getStatus()

Maven SNAPSHOT 版本

1.0-SNAPSHOT, 1.4.9-beta1-SNAPSHOT

表示一个快照版本。

Maven 风格范围

有多种选项可以在 Maven 风格中指示边界

  • [] 表示包含边界 → [1.1, 2.0]

  • () 表示排除边界 → (1.1, 2.0)(1.2, 1.5][1.1, 2.0)

  • ] 可以代替 ( 用于排除下限 → ]1.2, 1.5] 而不是 (1.2, 1.5]

  • [ 可以代替 ) 用于排除上限 → [1.1, 2.0[ 而不是 [1.1, 2.0)

理解版本排序

dependencies {
    implementation("org.springframework:spring-core:1.1") // This is a newer version than 1.a
    implementation("org.springframework:spring-core:1.a") // This is a older version than 1.1
}

版本排序用于

  • 确定特定版本是否包含在范围内

  • 在执行冲突解决时(使用“基本版本”)确定哪个版本是最新版本

版本根据以下规则排序

  • 将版本拆分为部分

    • 版本使用字符 [. - _ +] 分割成部分。

    • 包含数字和字母的部分会进一步拆分,例如 1a1 变成 1.a.1

    • 只比较部分,不比较分隔符,因此 1.a.1, 1-a+1, 1.a-1, 和 1a1 是等效的。(注意:在冲突解决期间有例外)。

  • 比较等效部分

    • 数字 vs. 数字: 较高的数字值被认为较高:1.1 < 1.2

    • 数字 vs. 非数字: 数字部分高于非数字部分:1.a < 1.1

    • 非数字 vs. 非数字: 部分按字母顺序和区分大小写进行比较:1.A < 1.B < 1.a < 1.b

    • 额外数字部分: 具有额外数字部分的版本更高,即使它为零:1.1 < 1.1.0

    • 额外非数字部分: 具有额外非数字部分的版本更低:1.1.a < 1.1

  • 特殊非数字部分

    • dev 低于任何其他非数字部分:1.0-dev < 1.0-ALPHA < 1.0-alpha < 1.0-rc

    • rcsnapshotfinalgareleasesp 高于任何其他字符串部分,顺序如下:1.0-zeta < 1.0-rc < 1.0-snapshot < 1.0-final < 1.0-ga < 1.0-release < 1.0-sp

    • 这些特殊值不区分大小写,它们的排序不依赖于使用的分隔符:1.0-RC-1 == 1.0.rc.1

声明富版本

当您使用简写表示法声明版本时,该版本被视为所需版本

build.gradle.kts
dependencies {
    implementation("org.slf4j:slf4j-api:1.7.15")
}
build.gradle
dependencies {
    implementation('org.slf4j:slf4j-api:1.7.15')
}

这意味着最低版本将是 1.7.15,并且引擎可以乐观地升级它。

要强制使用严格版本并确保只使用指定版本的依赖项,即使其他版本通常兼容也会被拒绝

build.gradle.kts
dependencies {
    implementation("org.slf4j:slf4j-api") {
        version {
            strictly("[1.7, 1.8[")
            prefer("1.7.25")
        }
    }
}
build.gradle
dependencies {
    implementation('org.slf4j:slf4j-api') {
        version {
            strictly '[1.7, 1.8['
            prefer '1.7.25'
        }
    }
}

Gradle 支持一种富版本声明模型,允许您组合不同级别的版本特异性。

关键术语,从强到弱排序,是

strictly!!

这是最强的版本声明。任何不匹配此表示法的版本都将被排除。如果用于声明的依赖项,strictly 可以降级版本。对于传递性依赖项,如果没有找到可接受的版本,依赖项解析将失败。

支持动态版本。

定义后,它将覆盖任何先前的 require 声明,并清除该依赖项上已声明的任何先前的 reject

require

这确保所选版本不能低于 require 所接受的版本,但可以通过冲突解决而更高,即使更高版本具有独占上限。这是直接依赖项的默认行为。

支持动态版本。

定义后,它将覆盖任何先前的 strictly 声明,并清除该依赖项上已声明的任何先前的 reject

prefer

这是最宽松的版本声明。它仅在没有指定更强的非动态版本时适用。

此术语不支持动态版本,并且可以补充 strictlyrequire

定义后,它将覆盖任何先前的 prefer 声明,并清除该依赖项上已声明的任何先前的 reject

此外,还有一个不在层次结构中的术语

reject

此术语指定模块不接受的版本,如果选择了被拒绝的版本,将导致依赖项解析失败。

支持动态版本。

富版本声明通过依赖项或约束声明上的 version DSL 方法访问,这使您能够访问 MutableVersionConstraint

build.gradle.kts
dependencies {
    implementation("org.slf4j:slf4j-api") {
        version {
            strictly("[1.7, 1.8[")
            prefer("1.7.25")
        }
    }

    constraints {
        add("implementation", "org.springframework:spring-core") {
            version {
                require("4.2.9.RELEASE")
                reject("4.3.16.RELEASE")
            }
        }
    }
}
build.gradle
dependencies {
    implementation('org.slf4j:slf4j-api') {
        version {
            strictly '[1.7, 1.8['
            prefer '1.7.25'
        }
    }

    constraints {
        implementation('org.springframework:spring-core') {
            version {
                require '4.2.9.RELEASE'
                reject '4.3.16.RELEASE'
            }
        }
    }
}

要强制使用严格版本,您也可以使用 !! 表示法

build.gradle.kts
dependencies {
    // short-hand notation with !!
    implementation("org.slf4j:slf4j-api:1.7.15!!")
    // is equivalent to
    implementation("org.slf4j:slf4j-api") {
        version {
           strictly("1.7.15")
        }
    }

    // or...
    implementation("org.slf4j:slf4j-api:[1.7, 1.8[!!1.7.25")
    // is equivalent to
    implementation("org.slf4j:slf4j-api") {
        version {
           strictly("[1.7, 1.8[")
           prefer("1.7.25")
        }
    }
}
build.gradle
dependencies {
    // short-hand notation with !!
    implementation('org.slf4j:slf4j-api:1.7.15!!')
    // is equivalent to
    implementation("org.slf4j:slf4j-api") {
        version {
           strictly '1.7.15'
        }
    }

    // or...
    implementation('org.slf4j:slf4j-api:[1.7, 1.8[!!1.7.25')
    // is equivalent to
    implementation('org.slf4j:slf4j-api') {
        version {
           strictly '[1.7, 1.8['
           prefer '1.7.25'
        }
    }
}

上面 [1.7, 1.8[!!1.7.25 的表示法等同于

  • 严格 [1.7, 1.8[

  • 偏好 1.7.25

这意味着引擎必须选择介于 1.7(包含)和 1.8(不包含)之间的版本。如果图中没有其他组件需要不同版本,则应偏好 1.7.25

严格版本无法升级并会覆盖任何传递依赖版本,因此建议将范围与严格版本一起使用。

下表说明了几个用例

此依赖项的哪些版本是可接受的? 严格 require prefer 拒绝 选择结果

用版本 1.5 测试;相信所有未来版本都应该可以工作。

1.5

1.5 开始的任何版本,等同于 org:foo:1.5。接受升级到 2.4

1.5 测试,软约束根据语义版本控制进行升级。

[1.0, 2.0[

1.5

介于 1.02.0 之间的任何版本,如果无人关心,则为 1.5。接受升级到 2.4
🔒

1.5 测试,但遵循语义版本控制。

[1.0, 2.0[

1.5

介于 1.02.0 之间(不包括 2.0)的任何版本,如果无人关心,则为 1.5
覆盖来自传递依赖项的版本。
🔒

同上,已知 1.4 已损坏。

[1.0, 2.0[

1.5

1.4

介于 1.02.0 之间(不包括 2.0)的任何版本,除了 1.4,如果无人关心,则为 1.5
覆盖来自传递依赖项的版本。
🔒

无意见,与 1.5 兼容。

1.5

如果无其他意见,则为 1.5,否则为任意版本。

无意见,偏好最新发布版本。

latest.release

构建时最新发布版本。
🔒

处于边缘,最新发布,不降级。

latest.release

构建时最新发布版本。
🔒

除了 1.5 之外没有其他版本。

1.5

1.5,如果另一个 strict 或更高 require 约束不一致则失败。
覆盖来自传递依赖项的版本。

1.5 或其补丁版本(独占)。

[1.5,1.6[

最新的 1.5.x 补丁版本,如果另一个 strict 或更高 require 约束不一致则失败。
覆盖来自传递依赖项的版本。
🔒

带锁(🔒)的行表示建议利用依赖锁定的情况。注意:使用依赖锁定后,始终建议发布已解析的版本

在库中使用 strictly 需要仔细考虑,因为它会影响下游消费者。然而,如果使用得当,它有助于消费者理解哪些库组合在他们的上下文中可能不兼容。有关更多详细信息,请参阅覆盖依赖版本部分。

富版本信息保留在 Gradle 模块元数据格式中。但是,将此信息转换为 Ivy 或 Maven 元数据格式会丢失信息。将发布最高级别的版本声明——strictlyrequire 优先于 prefer——并且任何 reject 都将被忽略。

支持严格版本

Gradle 通过选择依赖图中发现的最大版本来解决任何依赖版本冲突。有些项目可能需要偏离默认行为,强制使用较早版本的依赖项,例如,如果项目的源代码依赖于依赖项的较旧 API,而不是某些外部库。

通常,强制依赖是为了降级依赖。降级有常见的用例

  • 在最新版本中发现了错误。

  • 您的代码依赖于与新版本不二进制兼容的旧版本。

  • 您的代码不使用需要较新版本的库部分。

强制依赖的版本需要仔细考虑,因为更改传递依赖的版本可能会导致运行时错误,如果外部库需要不同版本。如果可能,通常最好将源代码升级到与新版本兼容。

假设一个项目使用 HttpClient执行 HTTP 调用。HttpClient 作为传递依赖引入了 Commons Codec 版本 1.10。然而,项目的生产源代码需要 Commons Codec 1.9 的 API,而该 API 在 1.10 中已不再可用。可以通过在构建脚本中将其声明为 strict 来强制依赖版本

build.gradle.kts
dependencies {
    implementation("org.apache.httpcomponents:httpclient:4.5.4")
    implementation("commons-codec:commons-codec") {
        version {
            strictly("1.9")
        }
    }
}
build.gradle
dependencies {
    implementation 'org.apache.httpcomponents:httpclient:4.5.4'
    implementation('commons-codec:commons-codec') {
        version {
            strictly '1.9'
        }
    }
}

使用严格版本的后果

使用严格版本必须仔细考虑

  • 对于库作者:严格版本实际上充当强制版本。它们优先于传递依赖项并覆盖传递发现的任何其他严格版本。如果消费者项目需要不同的版本,这可能导致构建失败。

  • 对于消费者:严格版本在解析过程中被全局考虑。如果严格版本与消费者的版本要求冲突,它将触发解析错误。

例如,如果项目 B 严格依赖于 C:1.0,但消费者项目 A 需要 C:1.1,则会发生解析错误。

为避免这种情况,建议使用版本范围和这些范围内的首选版本。

例如,B 可能不再说 strictly 1.0,而是严格依赖[1.0, 2.0[ 范围,但偏好 1.0。这样,如果消费者选择 1.1(或范围内的任何其他版本),构建将不再失败

不带版本声明

对于大型项目,建议不带版本声明依赖项并使用平台管理版本

build.gradle.kts
dependencies {
    implementation("org.springframework:spring-web")
}

dependencies {
    constraints {
        implementation("org.springframework:spring-web:5.0.2.RELEASE")
    }
}
build.gradle
dependencies {
    implementation 'org.springframework:spring-web'
}

dependencies {
    constraints {
        implementation 'org.springframework:spring-web:5.0.2.RELEASE'
    }
}

这种方法集中管理版本,包括传递依赖项。

声明动态版本

在许多情况下,您可能需要使用特定模块依赖项的最新版本,或某个版本范围内的最新版本。这在开发过程中或创建需要与各种依赖项版本兼容的库时通常是必要的。项目可能会采用更积极的方式使用依赖项,始终集成最新版本以访问尖端功能。

您可以通过使用动态版本轻松管理这些不断变化的依赖项。动态版本可以是版本范围(例如,2.+)或最新可用版本的占位符(例如,latest.integration

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

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework:spring-web:5.+")
}
build.gradle
plugins {
    id 'java-library'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework:spring-web:5.+'
}

使用动态版本和变更模块可能导致不可重现的构建。随着模块新版本的发布,其 API 可能与您的源代码不兼容。因此,请谨慎使用此功能。

为了可重现的构建,在声明动态版本依赖项时使用依赖锁定至关重要。否则,即使版本相同,您请求的模块也可能发生变化,这被称为变更版本。例如,Maven SNAPSHOT 模块总是指向最新发布的构件,使其成为一个“变更模块”。

声明变更版本

一个团队可能会在发布应用程序或库的新版本之前实现一系列功能。一种常见的策略是发布一个带有变更版本的模块,以允许消费者尽早集成其未完成的工件。变更版本表示功能集仍在积极开发中,尚未发布稳定的通用版本。

在 Maven 仓库中,变更版本通常被称为快照版本。快照版本包含后缀 -SNAPSHOT

以下示例演示了如何在 Spring 依赖项上声明快照版本

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

repositories {
    mavenCentral()
    maven {
        url = uri("https://repo.spring.io/snapshot/")
    }
}

dependencies {
    implementation("org.springframework:spring-web:5.0.3.BUILD-SNAPSHOT")
}
build.gradle
plugins {
    id 'java-library'
}

repositories {
    mavenCentral()
    maven {
        url = 'https://repo.spring.io/snapshot/'
    }
}

dependencies {
    implementation 'org.springframework:spring-web:5.0.3.BUILD-SNAPSHOT'
}

Gradle 足够灵活,可以将任何版本视为变更版本。您只需将属性 ExternalModuleDependency.setChanging(boolean) 设置为 true

文件依赖项的版本控制

建议在使用文件依赖项时明确表达意图并指定具体版本。

文件依赖项不被 Gradle 的版本冲突解决考虑。因此,将版本分配给文件名对于指示每个版本中包含的不同更改集至关重要。

例如,使用 commons-beanutils-1.3.jar 可以通过其发布说明跟踪库中的更改。

通过遵循此做法

  • 项目依赖项变得更容易维护和组织。

  • 通过指定版本更容易识别潜在的 API 不兼容性。