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

时间:2017-07-24 14:59:49

标签: java java-9 jigsaw

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

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

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

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

4 个答案:

答案 0 :(得分:12)

版本冲突

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

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

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

模块地狱?

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

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

答案 1 :(得分:4)

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

当您学习Jigsaw under the hood时,您会找到专门讨论此主题的整个部分。

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

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

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

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

答案 2 :(得分:1)

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

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

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

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

从运行时### BEGIN .ebextensions/logs.config option_settings: - namespace: aws:elasticbeanstalk:cloudwatch:logs option_name: StreamLogs value: true - namespace: aws:elasticbeanstalk:cloudwatch:logs option_name: DeleteOnTerminate value: false - namespace: aws:elasticbeanstalk:cloudwatch:logs option_name: RetentionInDays value: 7 files: "/etc/awslogs/config/stdout.conf": mode: "000755" owner: root group: root content: | [docker-stdout] log_group_name=/aws/elasticbeanstalk/environment_name/docker-stdout log_stream_name={instance_id} file=/var/log/eb-docker/containers/eb-current-app/*-stdouterr.log commands: "00_restart_awslogs": command: service awslogs restart ### END .ebextensions/logs.config 开始,没有大的变化,所有应用程序模块都由同一个类加载器加载,因此除非使用模块化,否则不可能在同一个jvm中使用不同版本的相同类像OSGI这样的框架或者自己操纵类加载器。

答案 3 :(得分:0)

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

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