JEP 260:封装大多数内部 API
概述
默认情况下,封装大多数 JDK 的内部 API,使得它们在编译时不可访问,并为未来版本中运行时也不可访问做好准备。确保关键且广泛使用的内部 API 不被封装,以便在所有或大部分功能有支持的替代方案之前,它们仍然可以被访问。
非目标
本 JEP 并未定义任何内部 API 的替代方案;这项工作或将会由单独的 JEP 以及(在适当的情况下)JSR 来涵盖。
本 JEP 并不承诺在各个版本之间保留任何内部 API 的兼容性;它们将继续保持不稳定状态,并可能在未通知的情况下发生变化。
动机
一些流行的库使用了非标准、不稳定且不受支持的 API,这些 API 是 JDK 的内部实现细节,并且从未打算用于外部使用。在模块化的 JDK 中(JEP 200),通过利用模块系统(JEP 261)限制对这些 API 的访问,可以提升平台的完整性和安全性,因为许多这些内部 API 定义了特权和与安全相关的操作。从长远来看,这一改变将降低 JDK 本身的维护者以及那些有意或无意中使用这些内部 API 的库和应用程序的维护者的成本。
描述
基于对各种大型代码集合(包括 Maven Central)的分析,以及自 JDK 8 及其依赖分析工具 (jdeps
) 发布以来收到的反馈,我们将 JDK 的内部 API 分为两大类:
-
非关键内部 API,这些 API 似乎没有被 JDK 外部的代码使用,或者仅为了方便而被外部代码使用,即用于在受支持的 API 中已提供的功能,或可以轻松通过库实现的功能(例如,
sun.misc.BASE64Decoder
);以及 -
关键内部 API,这些 API 提供了关键功能,这些功能很难(如果不是不可能的话)在 JDK 本身之外实现(例如,
sun.misc.Unsafe
)。
关键的内部 API 在 JDK 9 中是否被封装,取决于在 JDK 8 中是否存在支持的替代方案。支持的替代方案是指属于 Java SE 8 标准的一部分(即在 java.*
或 javax.*
包中),或者是特定于 JDK 并使用 @jdk.Exported
注解的内容(通常位于 com.sun.*
或 jdk.*
包中)。详细来说:
-
在 JDK 8 中存在支持的替代方案的关键内部 API 在 JDK 9 中被封装。
-
在 JDK 8 中不存在支持的替代方案的关键内部 API 在 JDK 9 中未被封装。下面提供了一个详细列表。
-
在 JDK 9 中存在支持的替代方案的关键内部 API 已被弃用,并将在未来的版本中被封装或移除。
JDK 9 中封装了所有非关键的内部 API。
在 JDK 9 中被封装的内部 API 在编译时是不可访问的。可以通过 --add-exports
命令行选项 使它们在编译时可访问。在运行时,如果这些 API 在 JDK 8 中是可访问的,则它们仍然保持可访问状态,但在未来的版本中,它们将变得不可访问。届时可以使用 --add-exports
或 --add-opens
选项使它们在运行时也可访问。--illegal-access
选项 控制这些 API 的运行时可访问性,并可用于模拟内部 API 在未来运行时的不可访问性。
JDK 9 中未封装的关键内部 API
此处列出了 JDK 9 中未封装的关键内部 API,因为 JDK 8 中不存在受支持的替代项。
-
sun.misc.{Signal,SignalHandler}
-
sun.misc.Unsafe
(这个类中的许多方法的功能可以通过 变量句柄 获得(JEP 193)。) -
sun.reflect.Reflection::getCallerClass(int)
(此方法的功能在由 JEP 259 定义的堆栈遍历 API 中可用。) -
sun.reflect.ReflectionFactory
-
com.sun.nio.file.{ExtendedCopyOption,ExtendedOpenOption, ExtendedWatchEventModifier,SensitivityWatchEventModifier}
这些 API 在 JDK 特定的 jdk.unsupported
模块中定义并导出。该模块存在于完整的 JRE 和 JDK 镜像中。因此,默认情况下,类路径上的代码可以访问这些 API,而模块中的代码如果声明了对 jdk.unsupported
模块的依赖,则也可以访问这些 API。
在 JDK 9 中引入替代方案的关键内部 API 在 JDK 9 中已被弃用,并将在未来的版本中被封装或移除。
jdk.unsupported
导出并开放 sun.misc
和 sun.reflect
包的一个后果是,这些包中的所有非关键内部 API 要么被移动到其他包,要么根据情况进行移除。不可升级的标准模块和 JDK 模块不应依赖于 jdk.unsupported
模块,而是应使用适当的内部 API。
使用 JDK 9 中已有替代 API 的关键内部 API 的库维护者可能希望使用多版本 JAR 文件 (JEP 238),以便发布单一工件,在 JDK 9 之前的版本中使用旧的 API,在更高版本中使用替代 API。
风险与假设
如果某个广泛使用的关键内部 API 未被确定为关键 API,并且该 API 被移动或删除,那么依赖它的应用程序将会失败。
如果某个广泛使用的关键内部 API 未被标识为关键但仍存在,则依赖它的应用程序可能会在本次发布中引发警告,并将在未来发布中失败。
这两种情况的短期解决方法是让最终用户通过上述命令行选项来暴露 API;从长远来看,在以后的版本中,可以将 API 移动到 jdk.unsupported
模块并导出以供外部使用。
之前在 sun.misc
和 sun.reflect
包中存在的非关键内部 API 已被移动或删除。依赖于它们的现有代码可能无法正常工作。
依赖
JEP 200(模块化的 JDK) 定义了 JDK 的模块化结构,而 JEP 261(模块系统) 则实现了模块系统。