在分离的Java平台模块中使用不同版本的依赖项

我预计可以在myModuleA中使用ie Guava-19,在myModuleB中使用guava-20, 因为jigsaw模块有自己的类路径。

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

与底层耦合的封装? 我找到了自己的理由。 由于传递依赖性会产生与以前相同的问题,因此无法支持它。 如果在ModuleA和ModuleB中的签名中发生版本冲突的guava类依赖于它。 应该使用哪个类?

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

版本冲突

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

对于所有应用程序类只有一个类加载器也解释了为什么不能有同一个类的两个版本:整个类加载基础结构是基于一个完全限定的类名称足以识别具有类加载器。

这也为多个版本打开了解决方案的路径。 就像之前你可以通过使用不同的类加载器来实现它。 模块系统本机方式是创建附加层 (每个层都有自己的加载器)。

模块地狱?

那么模块系统用模块地狱取代类路径地狱呢? 好吧,如果不创建新的类加载器,仍然无法使用同一个库的多个版本,因此这个基本问题仍然存在。

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

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

当您在引擎盖下学习Jigsaw时,您会找到专门讨论该主题的整个部分。

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

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

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

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

Java 9没有解决这些问题。 简而言之,在Java 9中所做的是将经典访问修饰符(public,protected,package-private,private)扩展到jar级别。

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

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

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

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