Java 的包管理系统在我看来总是简单而有效。它被 JDK 本身大量使用。我们一直在使用它来模仿命名空间和模块的概念。
拼图项目(又名Java 平台模块系统)试图填写什么?
来自官方网站:
该项目的目标是为 Java SE 平台设计和实现标准模块系统,并将该系统应用于平台本身和 JDK。
Java 的包管理系统在我看来总是简单而有效。它被 JDK 本身大量使用。我们一直在使用它来模仿命名空间和模块的概念。
拼图项目(又名Java 平台模块系统)试图填写什么?
来自官方网站:
该项目的目标是为 Java SE 平台设计和实现标准模块系统,并将该系统应用于平台本身和 JDK。
Jigsaw 和 OSGi 正在尝试解决相同的问题:如何允许粗粒度的模块进行交互,同时屏蔽它们的内部。
在 Jigsaw 的案例中,粗粒度模块包括 Java 类、包及其依赖项。
这是一个示例:Spring 和 Hibernate。两者都依赖于第 3 方 JAR CGLIB,但它们使用不同的、不兼容的 JAR 版本。如果你依赖标准的JDK,你能做什么?包括 Spring 想要打破 Hibernate 的版本,反之亦然。
但是,如果您有像 Jigsaw 这样的高级模型,您可以轻松地在不同模块中管理 JAR 的不同版本。将它们视为更高级别的包。
如果您从 GitHub 源代码构建 Spring,您也会看到它。他们重做了框架,因此它由几个模块组成:核心、持久性等。您可以挑选应用程序所需的最小模块依赖集,而忽略其余的。它曾经是一个单一的 Spring JAR,其中包含所有 .class 文件。
更新:五年后 - Jigsaw 可能仍有一些问题需要解决。
AFAIK 计划是让 JRE 更加模块化。即有较小的罐子是可选的和/或您可以只下载/升级您需要的功能。
它使它不那么臃肿,并让您可以选择删除可能大多数人不使用的遗留模块。
根据Mark Reinhold在比利时 Devoxx的主题演讲,Project Jigsaw将解决两个主要痛点:
我们都知道JAR 地狱。该术语描述了类加载过程最终无法工作的所有各种方式。类路径最广为人知的限制是:
JDK 的大单体性质导致了几个问题:
为了解决上述问题,我们将模块视为一种基本的新型 Java 程序组件。模块是一个命名的、自描述的代码和数据集合。它的代码被组织成一组包含类型的包,即Java 类和接口。它的数据包括资源和其他种类的静态信息。
为了控制其代码如何引用其他模块中的类型,模块声明它需要哪些其他模块才能编译和运行。为了控制其他模块中的代码如何引用其包中的类型,模块声明它导出哪些包。
模块系统定位所需的模块,并且与类路径机制不同,它确保模块中的代码只能引用它所依赖的模块中的类型。Java 语言和 Java 虚拟机的访问控制机制阻止代码访问包中的类型,这些类型不是由它们的定义模块导出的。
除了更可靠之外,模块化还可以提高性能。当模块中的代码引用包中的类型时,则保证该包被定义在该模块中或恰好在该模块读取的模块之一中。因此,在查找特定类型的定义时,无需在多个模块中搜索它,或者更糟的是,沿着整个类路径搜索。
Jigsaw 是一个巨大的项目,已经持续了好几年。它有大量的 JEP,这是获取有关该项目的更多信息的好地方。其中一些 JEP 如下:
在模块系统状态报告的初始版本中,Mark Reinhold 描述了模块系统的具体目标如下:
这些特性将直接或间接地使 Java SE 平台本身的应用程序开发人员、库开发人员和实现者受益,因为它们将支持可扩展的平台、更高的平台完整性和改进的性能。
为了论证,让我们断言 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.jar
which 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.jar
和bar-lib-2.2.jar
.
Jigsaw 没有解决这个问题。Mark Reinhold 在这里陈述了理由。要点是 Maven、Gradle 和其他工具代表了一个庞大的依赖管理生态系统,而另一种解决方案将弊大于利。
应该注意的是,其他解决方案(例如 OSGi)确实解决了这个问题(以及除了#4 之外的其他解决方案)。
底线
这是 Jigsaw 的一些关键点,由特定问题驱动。
请注意,解释 Jigsaw、OSGi、JBoss 模块等之间的争议是属于另一个 Stack Exchange 站点的单独讨论。解决方案之间的差异比此处描述的要多得多。更重要的是,对于 JSR 376的公共审查复议投票获得了足够的共识。
本文详细解释了 OSGi 和 JPMS/Jigsaw 都试图解决的问题:
“Java 9、OSGi 和模块化的未来” [2016 年 9 月 22 日]
它还深入探讨了 OSGi 和 JPMS/Jigsaw 的方法。截至目前,与成熟(16 岁)的 OSGi 相比,作者似乎几乎没有列出 JPMS/Jigsaw 的实用优点。