2024 年 Clojure 状态调查! 中分享您的想法。

欢迎!请查看 关于 页面以了解更多关于此工作的信息。

0
tools.deps

无法使用同一传递依赖项的不同版本。
这会变成可能吗?
Java 9(jigsaw)能帮忙吗?

1 答案

+2

被选中
 
最佳答案

默认情况下这是不可能的,jigsaw也无法帮助。然而,仍有希望。

在 JVM 中,默认的类加载器设置使用 Java 类路径。类路径是搜索(按顺序)类所在位置的列表。因此,如果类路径上有同一依赖项的两个版本,则会加载类路径中先发现的版本。

Java 9 中引入的模块系统引入了“模块”的新概念和新的“模块路径”,该路径指定要加载的基本模块集。模块提供了新的封装级别,防止外部使用实现的非公共部分,这可能很有用。然而,模块系统没有版本或版本选择的概念,因此包含同一模块多个版本的问题仍然存在。模块加载器会检测并对此问题提出抱怨,而不是您从类路径加载所得到的静默选择。但是,它并没有“修复”此问题。

当您使用Maven(或基于Maven的依赖解析系统,如Leiningen或Boot)时,您是在让Maven检查传递依赖的树,并从中选择一个版本来使用。在Maven中,选择基本上基于对传递树进行广度优先遍历后遇到的第一版本。tools.deps使用其自己的选择算法,优先选择依赖项的最新版本(同时仍然构建逻辑上合理的依赖树)。如果您遵循Rich在 Spec-ulation演讲中的建议,只在版本中进行增量且不破坏性的更改,那么这将大大降低意外版本选择导致的破坏性冲突。

如果您确实需要在同一时刻加载同一库的多个版本,Java类加载器架构确实允许您这么做,但这需要自定义类加载器和一些架构规范。在这种情况下,您创建一个接口(或协议),该接口从您的基类加载器加载,然后使用后续委托类加载器,只在那个类加载器中加载类的实现。后续委托允许每个独立的“插件”加载它需要的东西,但然后通过基本的接口与应用程序通信。正如我说的,这可行,但需要大量的设置和规范,并且对它的工作方式有一些限制,所以通常只有在您非常期望产品中包含具有高度依赖冲突可能性的“插件”型架构时才值得这么做。OSGi是解决这个问题的另一种方法。在Clojure中实现这些都不太愉快。

JVM团队似乎非常反对让用户加载同一类的多个版本的想法。他们甚至远到检测版本将被添加以防止用户在模块中嵌入版本的重命名。不清楚为什么。

我有一个疑问,tools.deps是否有一种模式,当发生冲突时直接失败,并使用显式声明来指定在这种情况下使用的版本?
在tools.deps中,顶层依赖版本始终使用,不考虑传递依赖中指定的任何其他版本。因此,您始终可以显式地在顶层声明依赖以“固定”特定版本。

有一些选项可以影响版本选择,但这会让其他人更难以了解在使用您的库时该做什么,所以我非常犹豫是否在clj中公开这个选项。

我甚至不确定“冲突”是什么意思 - 如果您正在谈论deps树中出现的同一库的多个版本,这在大多数非平凡的依赖集上基本都会发生(即使只是解决包含Clojure版本的版本)。
...