为什么 Gradle 或 Maven 没有依赖版本锁定文件?

Why doesn't Gradle or Maven have a dependency version lock file?

我最近在阅读 NPM、Yarn、Paket、Cargo 等包管理器时了解到依赖版本锁定文件的概念。我的理解是它是一个列出所有直接和可传递的文件依赖项及其确切的版本号,因此保证后续构建使用等效的依赖项集。这似乎是一个理想的功能,因为许多包管理器已经或正在采用这个概念。

那么我的问题是:

  1. 为什么 Maven 或 Gradle 不使用锁定文件?或者如果他们这样做了,为什么我没有看到它?

  2. 在包管理器的依赖项解析策略中允许版本范围与仅允许精确版本的优缺点是什么?

Maven、SBT 和 Gradle 都具有您所描述的内容。它被称为"using released (or fixed) versions"。与版本范围 [1.2.3,) 或快照 (1.2.3-SNAPSHOT) 相比,已发布版本看起来像 1.2.3

如果您所有的依赖项都使用已发布的版本,您将实现您所描述的。

版本范围也是一种有效的版本形式,具体取决于您的用例,但我通常建议不要使用它们,除非它们用于父级 POM-s,或仅在积极开发期间使用.当您不想继续更新第三方或父 POM 的固定版本时,版本范围会派上用场,如果您确定相应的工件不会对您造成任何破坏(并且,相信我,这在版本范围内经常发生)。当您希望保证代码将按照您最初设计和测试的内容构建和工作时,应使用固定版本。

如果您的 pom.xml 严格定义了依赖项的版本,则不需要 "lock file" 或类似的功能。

如果你阅读有关依赖管理的文档,你会发现确实如此:

  1. Maven 没有 有办法实现您的要求。即使您为直接依赖项设置了特定版本(您应该这样做),您的传递依赖项也很容易由于看似无关的更改而无意中更改。例如,添加对新库的依赖可以为您提供现有传递依赖的旧版本。

    您需要的是 dependencyManagement 部分列出您所有的直接和传递依赖项。您仍然无法检测传递依赖项是否被删除或添加,这是例如 NPM 提供的功能。缺少的问题是您的所有依赖项不再位于 dependencyManagement 部分中。要检测这些更改,您可以使用我写的 dependency-lock-maven-plugin 之类的东西。使用它还可以降低将所有内容都放在 dependencyManagement 部分中的重要性,因为将检测到传递依赖项中的更改。

    我还建议在您的构建中使用 https://maven.apache.org/enforcer/enforcer-rules/requireUpperBoundDeps.html,因为 Maven 选择在树中关闭的传递依赖项的版本,而不是您所期望的最高版本。

    我见过很多由于开发人员不小心更改传递依赖项而导致的运行时问题。

    TL;DR:您 确实 需要 Maven 中的锁文件之类的东西,但由于历史意识形态原因,它不存在。

  2. 我不建议使用版本范围,因为它们会使您的构建不可重现。 在传递依赖性方面,它的行为也不像您所相信的那样。

Dependency locking 是一个在 Gradle 5.0 之前达到一定程度成熟的特性: https://docs.gradle.org/current/userguide/dependency_locking.html

Gradle 的实现受到 Nebula 插件的启发:https://github.com/nebula-plugins/gradle-dependency-lock-plugin

当用作任何更新锁定机制的输入时,版本范围确实有效。因此,对于 Gradle,您实际上可以只针对特定的依赖项来解决您指定的版本范围:

gradle classes --update-locks org.apache.commons:commons-lang3,org.slf4j:slf4j-api

或者,您可以只说 "go update all my deps":

gradle dependencies --write-locks

如果您正在研究自动化,指定解析策略也值得回顾:https://docs.gradle.org/current/userguide/dependency_resolution.html