在单独的 Java 平台模块中使用不同版本的依赖项版本冲突

我期望可以在myModuleA中使用番石榴-19,在myModuleB中使用番石榴-20,因为拼图模块有自己的类路径。

假设 myModuleA 使用 Iterators.emptyIterator();- 在番石榴-20中被删除,myModuleB使用新的静态方法FluentIterable.of();- 在番石榴-19中没有。不幸的是,我的测试是阴性的。在编译时,它看起来很好。与运行时相反,结果是NoSuchMethodError。这意味着,类装入器上第一个类决定哪一个失败。

与底层耦合的封装?我为自己找到了一个理由。它无法得到支持,因为传递依赖关系将具有与以前相同的问题。如果一个番石榴类在模块A和模块B的签名中发生了版本冲突,则依赖于它。应该使用哪个类?

但是为什么在整个互联网上我们都可以读到“拼图 - 模块系统停止了类路径地狱”?我们现在有多个较小的“类似于类的路径”,它们具有相同的问题。这与其说是一个问题,不如说是一个不确定性。


答案 1

版本冲突

首先是更正:你说模块有自己的类路径,这是不正确的。应用程序的类路径保持原样。与它并行的是模块路径,但它基本上以相同的方式工作。特别是,所有应用程序类都由同一类装入器装入(至少缺省情况下)。

所有应用程序类只有一个类装入器,这也解释了为什么不能有同一类的两个版本:整个类装入基础结构都是基于一个假设构建的,即完全限定的类名足以使用类装入器来标识类。

这还会打开多个版本的解决方案的路径。就像以前一样,你可以通过使用不同的类装入器来实现这一点。模块系统本机方法是创建其他层(每都有自己的加载器)。

模块地狱?

那么模块系统会用模块地狱取代类路径地狱吗?好吧,如果不创建新的类装入器,同一库的多个版本仍然是不可能的,所以这个基本问题仍然存在。

另一方面,现在由于拆分包,您至少在编译或启动时会遇到错误。这可以防止程序微妙地行为不端,这也不是那么糟糕。


答案 2

从理论上讲,可以在应用程序中使用一库的不同版本。实现这一目标的概念是:分层

当你在引擎盖下研究Jigsaw时,你会发现一整节专门讨论这个主题。

这个想法基本上是你可以使用这些层进一步对模块进行分组。层是在运行时构造的;他们有自己的类装入器。含义:在一个应用程序中使用不同版本的模块应该是绝对可能的 - 它们只需要进入不同的层。如图所示 - 这种“多版本支持”是由java / jigsaw工作的人积极讨论的。这不是一个晦涩难懂的功能 - 它旨在支持一个引擎盖下的不同模块版本。

在这一点上唯一的免责声明:不幸的是,那里没有“完整”的源代码示例(我知道),因此我只能链接到Oracle演示文稿。

换句话说:这种版本控制问题即将出现某种解决方案-但是要用这个新想法在现实世界的代码中进行体验还需要更多时间。确切地说:您可以拥有由不同类装入器隔离的不同层。没有支持允许您“同一对象”同时使用modV1和modV2。您只能有个对象,一个使用 modV1,另一个使用 modV2。

(德国读者可能想看看这里 - 该出版物包含对图层主题的另一个介绍)。


推荐