为什么要设计拼图/ JPMS?


79

Java的程序包管理系统对我而言似乎总是简单而有效的。JDK本身大量使用它。我们一直在使用它来模仿名称空间和模块的概念。

什么是Jigsaw项目(又名Java平台模块系统)试图填补?

从官方网站:

该项目的目标是为Java SE平台设计和实现标准模块系统,并将该系统应用于平台本身和JDK。

Answers:


100

Jigsaw和OSGi试图解决相同的问题:如何在保护内部组件的同时允许粗粒度模块进行交互。

在Jigsaw的情况下,较粗粒度的模块包括Java类,程序包及其依赖项。

这是一个示例:Spring和Hibernate。两者都依赖于第三方JAR CGLIB,但是它们使用该JAR的不同,不兼容的版本。如果您依赖标准JDK怎么办?包括Spring想要破解Hibernate的版本,反之亦然。

但是,如果您拥有像Jigsaw这样的高级模型,则可以在不同的模块中轻松管理不同版本的JAR。将它们视为更高级别的软件包。

如果您从GitHub源码构建Spring,您也会看到它。他们已经重做了框架,因此它由几个模块组成:核心,持久性等。您可以选择应用程序所需的最小模块依赖项集,而忽略其余部分。它曾经是单个Spring JAR,其中包含所有.class文件。

更新:五年后-拼图可能仍然需要解决一些问题


5
如果您仍然需要使用完全相同的模块,但使用两个不同的版本怎么办?他们不应该只是添加某种支持,以便同一类的两个版本可以共存吗?
Didier A.

7
鉴于实际计划在Java 9中发布的内容,该帖子具有误导性。在撰写本文时,它可能是准确的。
xenoterracide '16

1
mreinhold.org/blog/jigsaw-complete项目完成并发布了针对Java 9
虫Zasz

@xenoterracide,您不能指责某人没有千里眼。该职位比Java 9提前了五年。您是否也在审查Jon Skeet的每个答案?
duffymo

这篇文章的年龄不够好。Java模块故意无法解决版本问题,请参见此线程。仍然没有简单的方式来解决我们的老朋友NoSuchMethodError和朋友NoClassDefFoundError
塔马斯·赫格杜斯

45

AFAIK计划是使JRE更具模块化。即具有较小的jar,它们是可选的,并且/或者您只能下载/升级所需的功能。

它可以减少膨胀,并为您提供删除大多数人可能不使用的旧模块的选项。


7
可接受的答案是有效的,但此答案更好,因为它可以说明实际的预期效果。+1,当之无愧。
Silviu Burcea 2014年

我很好奇,这是否也意味着如果我将Google Guava作为依赖项,但是我仅在其中使用ImmutableList,那么我只能导入ImmutableList依赖项而将其余的Guava类排除在外?
tmn

1
@ThomasN。当您使用import所有这些方法时,是将该类引入编译器的类的名称空间。如果您实际上不使用它,它将不会出现在创建的字节码中。如果您实际使用该类,则需要该类及其在运行时使用的每个类。从理论上讲,您可以创建仅具有所需内容的guava API的简化版本,而改用该JAR。实际上,这很容易出错,并且在大多数情况下不是很有用,最终您只能添加已发布的整个JAR。
彼得·劳瑞

43

根据Mark Reinhold在比利时Devoxx上主题演讲拼图计划将解决两个主要的痛点:

  1. 类路径
  2. 大规模单片JDK

Classpath怎么了?

我们都知道JAR地狱。该术语描述了导致类加载过程最终无法正常工作的所有各种方式。类路径的最著名的限制是:

  • 很难判断是否存在冲突。像maven这样的构建工具可以根据工件名称来做得很好,但是如果工件本身具有不同的名称但内容相同,则可能会发生冲突。
  • jar文件的根本问题是它们不是组件。它们只是一堆将线性搜索的文件容器。Classpath是一种查找类的方法,无论它们位于哪个组件中,位于哪个程序包中或它们的预期用途如何。

大规模单片JDK

JDK的巨大整体性导致几个问题:

  • 它不适用于小型设备。即使小型IoT类型的设备具有能够运行SE类VM的处理器,但它们不一定具有容纳所有JDK的内存,尤其是在应用程序仅使用其中一部分的情况下。
  • 甚至在云中也是一个问题。云就是要优化硬件的使用,如果您有成千上万个包含整个JDK的图像,但是应用程序仅使用其中的一小部分,那将是浪费。

模块:通用解决方案

为了解决上述问题,我们将模块视为一种基本的新型Java程序组件。模块是命名的,自描述的代码和数据集合。它的代码被组织为一组包含类型(即Java类和接口)的软件包。其数据包括资源和其他种类的静态信息。

为了控制其代码如何引用其他模块中的类型,模块声明要编译和运行它需要哪些其他模块。为了控制其他模块中的代码如何引用其包中的类型,模块声明要导出的包中的哪个。

模块系统查找所需的模块,并且与类路径机制不同,它确保模块中的代码只能引用其所依赖的模块中的类型。Java语言和Java虚拟机的访问控制机制可防止代码访问其定义模块未导出的程序包中的类型。

除了更可靠之外,模块化还可以提高性能。当模块中的代码引用包中的类型时,则可以保证在该模块中定义该包,或者恰好在该模块读取的一个模块中定义该包。因此,在查找特定类型的定义时,无需在多个模块中或更糟的是在整个类路径中进行搜索。

遵循的JEP

拼图是一个巨大的项目,已经进行了很多年。它包含大量令人印象深刻的JEP,这些都是获得有关该项目更多信息的好地方。其中一些JEP如下:

闭幕致辞

模块系统状态报告的初始版本中,Mark Reinhold描述了模块系统的特定目标,如下所示:

  • 可靠的配置,用一种使程序组件声明相互之间显式依赖的方法来替换脆弱的,容易出错的类路径机制。
  • 强封装,允许组件声明其他组件可访问的公共类型,而其他组件则不能访问。

这些功能将直接或间接地使应用程序开发人员,库开发人员和Java SE Platform本身的实现者受益,因为它们将使可伸缩平台,更高的平台完整性和更高的性能成为可能。


3
马克·雷因霍尔德(Mark Reinhold)是Oracle Java平台小组的首席架构师,这个答案本质上是他对这个确切问题的直接答案。
杰伊

1
您可以量化一下,HelloWorld可以使用15 MB而不是553 MB。youtu.be/rFhhLXcOBsk?t=31m12s
user1133275

14

为了争辩,我们断言Java 8(及更早版本)已经具有模块(罐)和模块系统(类路径)的“形式”。但是这些都有一些众所周知的问题。

通过研究问题,我们可以说明拼图的动机。(以下假设我们没有使用OSGi,JBoss Modules等,它们肯定会提供解决方案。)

问题一:公众公开

考虑以下类(假设两者都是公共的):

com.acme.foo.db.api.UserDao
com.acme.foo.db.impl.UserDaoImpl

在Foo.com,我们可能会决定我们的团队应该使用UserDao而不是UserDaoImpl直接使用。但是,没有办法在类路径上强制执行该操作。

在Jigsaw中,一个模块包含一个module-info.java文件,该文件使我们可以明确声明对其他模块公开的内容。也就是说,公众具有细微差别。例如:

// com.acme.foo.db.api.UserDao is accessible, but
// com.acme.foo.db.impl.UserDaoImpl is not 
module com.acme.foo.db {
    exports com.acme.foo.db.api;
}

问题2:反射无节制

给定#1中的类,仍然可以在Java 8中执行此操作:

Class c = Class.forName("com.acme.foo.db.impl.UserDaoImpl");
Object obj = c.getConstructor().newInstance();

就是说:反射是强大且必不可少的,但是如果不加以检查,它可以以不希望的方式用于进入模块的内部。马克·莱因霍尔德(Mark Reinhold)有一个令人震惊的例子。(SO帖子在这里。)

在Jigsaw中,强封装提供了拒绝访问类(包括反射)的能力。(这可能取决于命令行设置,有待修订的JDK 9技术规范。)请注意,由于Jigsaw用于JDK本身,因此Oracle声称这将使Java团队能够更快地创新平台内部。

问题3:类路径消除了架构关系

团队通常具有关于罐子之间关系的心理模型。例如,foo-app.jar可以使用foo-services.jar其中的用途foo-db.jar。我们可能会断言in中的类foo-app.jar不应绕过“服务层”而foo-db.jar直接使用。但是,没有办法通过类路径强制执行该操作。马克·莱因霍尔德(Mark Reinhold)在这里提到了这个问题

相比之下,Jigsaw为模块提供了明确,可靠的可访问性模型。

问题4:整体运行时

Java运行时位于整体中rt.jar。在我的机器上,它是60+ MB的20k类!在微服务,物联网设备等时代,如果不使用Corba,Swing,XML和其他库,则不希望在磁盘上使用它们。

拼图将JDK本身分为许多模块。例如,java.sql包含熟悉的SQL类。这样做有很多好处,但是一种新的jlink工具。假设应用程序已完全模块化,则jlink生成可分发的运行时映像,该映像被裁剪为仅包含指定的模块(及其依赖项)。展望未来,Oracle展望了将JDK模块提前编译为本机代码的未来。尽管jlink是可选的,并且AOT编译是试验性的,但它们是Oracle前进方向的主要指示。

问题5:版本控制

众所周知,类路径不允许我们使用同一jar的多个版本:egbar-lib-1.1.jarbar-lib-2.2.jar

拼图没有解决这个问题。马克·雷因霍尔德(Mark Reinhold)在这里陈述了基本原理。要点是,Maven,Gradle和其他工具代表了一个庞大的依赖管理生态系统,而另一种解决方案则弊大于利。

应当注意,其他解决方案(例如OSGi)确实解决了这个问题(除了#4,还有其他解决方案)。

底线

这是拼图的一些关键点,受特定问题的影响。

注意,解释Jigsaw,OSGi,JBoss Modules等之间的争议是一个单独的讨论,它属于另一个Stack Exchange站点。解决方案之间的差异比这里描述的要多得多。而且,已经有足够的共识来批准JSR 376的“公共审核重新审议”投票


3

本文详细解释了OSGi和JPMS / Jigsaw都试图解决的问题:

“ Java 9,OSGi和模块化的未来” [2016年9月22日]

它还深入探讨了OSGi和JPMS / Jigsaw的方法。到目前为止,与成熟的OSGi(16岁)相比,作者似乎几乎没有列出JPMS / Jigsaw的实用Pro。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.