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

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.11-a+11.a-11a1 是等效的。(注意:冲突解决期间存在例外)。

  • 比较等效部分

    • 数值与数值:数值较高的被认为更高:1.1 < 1.2

    • 数值与非数值:数值部分高于非数值部分:1.a < 1.1

    • 非数值与非数值:部分按字母顺序和区分大小写进行比较: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 等效于

  • strictly [1.7, 1.8[

  • prefer 1.7.25

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

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

下表说明了几个用例

此依赖项的哪些版本是可以接受的? strictly require prefer rejects 选择结果

使用版本 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 (排除) 之间的任何版本,1.5 如果没有其他人关心。
覆盖来自传递依赖项的版本。
🔒

与上面相同,1.4 已知损坏。

[1.0, 2.0[

1.5

1.4

1.02.0 (排除) 之间的任何版本,除了 1.41.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 strictly 依赖于 C:1.0,但消费者项目 A 需要 C:1.1,则会发生解析错误。

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

例如,B 可能会说,而不是 strictly 1.0,它strictly 依赖于 [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 即可。