80

Java 的包管理系统在我看来总是简单而有效。它被 JDK 本身大量使用。我们一直在使用它来模仿命名空间和模块的概念。

拼图项目(又名Java 平台模块系统)试图填写什么?

来自官方网站:

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

4

5 回答 5

101

Jigsaw 和 OSGi 正在尝试解决相同的问题:如何允许粗粒度的模块进行交互,同时屏蔽它们的内部。

在 Jigsaw 的案例中,粗粒度模块包括 Java 类、包及其依赖项。

这是一个示例:Spring 和 Hibernate。两者都依赖于第 3 方 JAR CGLIB,但它们使用不同的、不兼容的 JAR 版本。如果你依赖标准的JDK,你能做什么?包括 Spring 想要打破 Hibernate 的版本,反之亦然。

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

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

更新:五年后 -​​ Jigsaw 可能仍有一些问题需要解决。

于 2012-08-07T11:27:14.930 回答
45

AFAIK 计划是让 JRE 更加模块化。即有较小的罐子是可选的和/或您可以只下载/升级您需要的功能。

它使它不那么臃肿,并让您可以选择删除可能大多数人不使用的遗留模块。

于 2012-08-07T11:27:33.413 回答
44

根据Mark Reinhold在比利时 Devoxx的主题演讲Project Jigsaw将解决两个主要痛点:

  1. 类路径
  2. 庞大的单体 JDK

类路径有什么问题?

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

  • 很难说有没有冲突。像 maven 这样的构建工具可以根据工件名称做得很好,但是如果工件本身具有不同的名称但内容相同,则可能会发生冲突。
  • jar 文件的根本问题是它们不是组件。它们只是一堆将被线性搜索的文件容器。类路径是一种查找类的方法,无论它们在什么组件中、它们在什么包中或它们的预期用途如何。

庞大的单体 JDK

JDK 的大单体性质导致了几个问题:

  • 它不适合小型设备。尽管小型 IoT 类型的设备具有能够运行 SE 类 VM 的处理器,但它们不一定具有容纳所有 JDK 的内存,尤其是当应用程序只使用它的一小部分时。
  • 这甚至是云中的一个问题。云就是为了优化硬件的使用,如果你有成千上万个包含整个 JDK 的图像,但应用程序只使用了其中的一小部分,那将是一种浪费。

模块:通用解决方案

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

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

模块系统定位所需的模块,并且与类路径机制不同,它确保模块中的代码只能引用它所依赖的模块中的类型。Java 语言和 Java 虚拟机的访问控制机制阻止代码访问包中的类型,这些类型不是由它们的定义模块导出的。

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

跟随的 JEP

Jigsaw 是一个巨大的项目,已经持续了好几年。它有大量的 JEP,这是获取有关该项目的更多信息的好地方。其中一些 JEP 如下:

结束语

在模块系统状态报告的初始版本中,Mark Reinhold 描述了模块系统的具体目标如下:

  • 可靠的配置,用一种程序组件声明彼此显式依赖的方法来替换脆弱、容易出错的类路径机制,以及
  • 强封装,允许组件声明它的哪些公共类型可以被其他组件访问,哪些不能。

这些特性将直接或间接地使 Java SE 平台本身的应用程序开发人员、库开发人员和实现者受益,因为它们将支持可扩展的平台、更高的平台完整性和改进的性能。

于 2016-01-15T14:47:41.793 回答
14

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

通过检查问题,我们可以说明 Jigsaw 的动机。(以下假设我们没有使用 OSGi、JBoss 模块等,它们肯定会提供解决方案。)

问题一:public公开了

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

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:类路径删除了架构关系

一个团队通常有一个关于 jar 之间关系的心智模型。例如,foo-app.jar可以使用foo-services.jarwhich uses foo-db.jar。我们可以断言,类foo-app.jar不应绕过“服务层”而foo-db.jar直接使用。但是,没有办法通过类路径来强制执行。Mark Reinhold在这里提到了这一点

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

问题 4:单片运行时

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

Jigsaw 将JDK 本身分解为许多模块;例如java.sql包含熟悉的 SQL 类。这样做有几个好处,但一个新的好处是jlink工具。假设一个应用程序是完全模块化的,jlink生成一个可分发的运行时映像,该映像被修剪为仅包含指定的模块(及其依赖项)。展望未来,Oracle设想了一个将 JDK 模块提前编译为本机代码的未来。虽然jlink是可选的,而且 AOT 编译是实验性的,但它们是 Oracle 发展方向的主要迹象。

问题 5:版本控制

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

Jigsaw 没有解决这个问题。Mark Reinhold 在这里陈述了理由。要点是 Maven、Gradle 和其他工具代表了一个庞大的依赖管理生态系统,而另一种解决方案将弊大于利。

应该注意的是,其他解决方案(例如 OSGi)确实解决了这个问题(以及除了#4 之外的其他解决方案)。

底线

这是 Jigsaw 的一些关键点,由特定问题驱动。

请注意,解释 Jigsaw、OSGi、JBoss 模块等之间的争议是属于另一个 Stack Exchange 站点的单独讨论。解决方案之间的差异比此处描述的要多得多。更重要的是,对于 JSR 376的公共审查复议投票获得了足够的共识。

于 2017-06-15T18:03:20.123 回答
3

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

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

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

于 2016-11-11T06:20:35.313 回答