JEP 200:模块化的 JDK
概述
目标
将 JDK 分成一组模块,这些模块可以在编译时、构建时和运行时组合成各种配置,包括但不限于:
-
对应于完整的 Java SE 平台、完整的 JRE 和完整的 JDK 的配置;
-
内容大致等同于 Java SE 8 中定义的每个 Compact Profiles 的配置;以及
-
自定义配置,仅包含一组指定的模块,可能还会增加外部库和应用程序模块,以及所有这些模块传递依赖所需的模块。
模块化结构的定义应该明确区分其规范由 Java 社区进程 管理的标准模块,和特定于 JDK 的模块。它还应该区分包含在 Java SE 平台规范中的模块(因此在每个平台实现中都是强制性的)与所有其他模块。
动机
Project Jigsaw 旨在为 Java SE 平台设计并实现一个标准的模块系统,并将该系统应用于平台本身以及 JDK。其主要目标是使平台的实现更易于扩展到小型设备,提升安全性和可维护性,实现更高的应用程序性能,并为开发者提供用于大型编程的更好工具。
描述
设计原则
JDK 的模块化结构实现了以下原则:
-
标准模块的规范由 JCP 管理,其名称以字符串
"java."
开头。 -
所有其他模块仅仅是 JDK 的一部分,其名称以字符串
"jdk."
开头。 -
如果一个模块导出了一个包含某种类型(该类型又包含一个公共或受保护成员,并且该成员引用了来自其他模块的某种类型)的包,那么第一个模块必须通过
requires transitive
向第二个模块授予隐式可读性。(这确保了方法调用链能够以显而易见的方式工作。) -
一个标准模块可以同时包含标准和非标准的 API 包。如果一个标准模块导出了一个标准 API 包,则该导出可以是限定的;如果一个标准模块导出了一个非标准 API 包,则该导出必须是限定的。无论哪种情况,如果一个标准模块以限定方式导出了某个包,则该导出必须针对 JDK 中的一部分模块。如果一个标准模块是一个 Java SE 模块(即,包含在 Java SE 平台规范中),则它不得导出任何非 SE API 包,至少不能无限制地导出。
-
一个标准模块可以依赖于一个或多个非标准模块。它不得向任何非标准模块授予隐式可读性。如果它是 Java SE 模块,则它不得向任何非 SE 模块授予隐式可读性。
-
一个非标准模块不得导出任何标准 API 包。非标准模块可以向标准模块授予隐式可读性。
原则 4 和原则 5 的一个重要结果是,仅依赖于 Java SE 模块的代码将只依赖于标准的 Java SE 类型,因此可以移植到所有 Java SE 平台的实现。
模块图
JDK 的模块化结构可以被可视化为一个图表:每个模块是一个节点,如果一个模块依赖于另一个模块,则从第一个模块到第二个模块会有一条有向边。完整的模块图包含了过多的边,难以轻松展示;这里是该图的传递约简版本,其中冗余的边被省略了(点击放大):
以下是模块图的导览:
-
标准的 Java SE 模块用橙色表示;非 SE 模块用蓝色表示。
-
如果一个模块依赖于另一个模块,并且它对该模块授予了隐式可读性,那么从第一个模块到第二个模块的边为实线;否则,边为虚线。
-
在最底部是
java.base
模块,其中包含基本类,例如java.lang.Object
和java.lang.String
。基础模块不依赖任何模块,而其他所有模块都依赖基础模块。指向基础模块的边比其他边更浅。 -
靠近顶部的是
java.se.ee
模块,它汇集了构成 Java SE 平台的所有模块,包括与 Java EE 平台规范重叠的模块。这是一个聚合器模块的例子,它收集并重新导出其他模块的内容,但不添加自己的内容。配置为包含java.se.ee
模块的运行时系统将包含 Java SE 平台的所有 API 包。一个模块当且仅当它是从java.se.ee
模块可达的标准模块时,才被纳入 Java SE 平台规范。 -
java.se
聚合器模块汇集了 Java SE 平台中不与 Java EE 重叠的部分。 -
非标准模块包括调试和服务工具及 API(例如,
jdk.jdi
、jdk.jcmd
和jdk.jconsole
),开发工具(例如,jdk.compiler
、jdk.javadoc
和jdk.xml.bind
),以及各种服务提供者(例如,jdk.charsets
、jdk.scripting.nashorn
和jdk.crypto.ec
),这些通过现有的[java.util.ServiceLoader](http://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html)
机制提供给其他模块。 -
java.smartcardio
模块是标准的,但不是 Java SE 平台规范的一部分,因此其名称以字符串"java."
开头,但显示为蓝色,并且无法从java.se
模块访问。
模块图实际上是一种新型的 API,因此被这样指定和发展。以 java.se.ee
模块为根的模块图子图删除了所有非 SE 模块及相应的边,这部分在 Java SE 平台规范中有详细规定;其后续发展将由 JCP(Java Community Process)管理。模块图其余部分的发展将由未来的 JEP(JDK Enhancement Proposal)涵盖。无论在哪种情况下,如果某个模块被指定为可供通用,那么它将受到与其他 API 相同的演化约束。特别是移除这样的模块或对其进行不兼容的更改时,必须至少提前一个主版本发布公开通知。
包含 Linux/AMD64 构建的占用空间指标的所有模块的表格摘要可在此处获取:链接。
测试
JDK 和用于运行测试的工具 [jtreg](http://openjdk.java.net/jtreg/)
中的单元测试和回归测试现在允许根据测试所涉及的模块及其依赖的模块来选择测试,从而可以测试任意配置的 JDK 模块。
此增强功能的主要功能测试检查一组配置的模块,以确保其是本文定义的模块的有效组合,每个模块都具有预期的内容并导出预期的 API 包,并且这些模块具有预期的依赖关系。
JCK 现在可以测试模块图中成为 Java SE 平台规范组成部分的那些方面。这包括 SE 模块的名称、它们导出的 API 包以及导致 SE API 包被重新导出的相互依赖关系。JCK 还可以测试平台实现中存在的 SE 模块的任意配置。
风险与假设
这里定义的模块化结构不支持至少一种已知的用例,即希望使用 java.beans
包而不必依赖非常庞大的 java.desktop
模块。它可能无法解决其他目前未知的用例。如果在该 JEP 的最终实现中未支持某个关键用例,我们预计可以通过重构模块图在后续版本中解决该问题。
依赖
此 JEP 是 Project Jigsaw 的若干 JEP 之一。其他 JEP 包括: