15

我希望可以在 myModuleA 中使用 Guava-19 和在 myModuleB 中使用 guava-20,因为拼图模块有自己的类路径。

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

与底层耦合的封装?我为自己找到了一个理由。它不能被支持,因为传递依赖会和以前有同样的问题。如果在 ModuleA 和 ModuleB 的签名中发生了版本冲突的番石榴类依赖于它。应该使用哪个类?

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

4

4 回答 4

19

版本冲突

首先更正:您说模块有自己的类路径,这是不正确的。应用程序的类路径保持原样。与之平行的是引入了模块路径,但它基本上以相同的方式工作。特别是,所有应用程序类都由同一个类加载器加载(至少默认情况下)。

所有应用程序类只有一个类加载器,这也解释了为什么不能有同一个类的两个版本:整个类加载基础结构是建立在这样的假设之上的:一个完全限定的类名足以识别一个具有类加载器。

这也为多个版本打开了解决方案的道路。就像以前一样,您可以通过使用不同的类加载器来实现这一点。执行此操作的模块系统本机方法是创建额外的(每个层都有自己的加载器)。

模块地狱?

那么模块系统是否将类路径地狱替换为模块地狱?好吧,如果不创建新的类加载器,同一个库的多个版本仍然是不可能的,所以这个基本问题仍然存在。

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

于 2017-07-24T16:26:09.237 回答
8

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

当您深入研究Jigsaw时,您会发现一个专门针对该主题的部分。

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

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

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

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

于 2017-07-25T07:27:10.617 回答
2

Java 9 没有解决这些问题。简而言之,Java 9 所做的是将经典的访问修饰符(公共、受保护、包私有、私有)扩展到 jar 级别。

在 java 9 之前,如果模块 A 依赖于模块 B,则 B 中的所有公共类对 A 都是可见的。

使用 Java 9,可以配置可见性,因此它可以仅限于类的子集,每个模块都可以定义哪些包导出以及哪些包需要。

大多数检查是由编译器完成的。

从运行时来看perspective(classloader architecture),没有大的变化,所有的应用程序模块都是由同一个类加载器加载的,所以在同一个jvm中不可能有不同版本的同一个类,除非你使用像OSGI这样的模块化框架或者自己操作类加载器.

于 2018-04-05T21:41:17.313 回答
0

正如其他人所暗示的那样,JPMS 层可以帮助解决这个问题。您可以手动使用它们,但Layrry可能对您有所帮助,它是一个流畅的 API 和基于配置的启动器,用于运行分层应用程序。它允许您通过配置来定义层结构,它会为您启动层图。它还支持在运行时动态添加/删除层。

免责声明:我是 Layrry 的最初创建者

于 2020-05-27T10:12:46.757 回答