作者 Mark Reinhold
所有者 Chris Hegarty
创建 2015/08/03 18:29
更新 2017/03/08 13:58
类型 功能
状态 已完成
域 JDK
讨论 拼图的开发在 openjdk.java.net
努力 M
持续时间 L
优先 1
检验人 Alan Bateman, Alex Buckley, Brian Goetz, John Rose, Paul Sandoz
支持 Brian Goetz
发行 9
版本 8132928
概要
将大部分 JDK 内部 API 修改为默认不可访问,只保留一些十分重要,被广泛使用的 API 的可访问性,直到替代这些 API 全部或大部分功能的代码被实现。
非目标
此 JEP 并不会对任何内部 API 的替代方案提出建议,那一任务将交由单独的 JEP,或时机恰当时, 由 JSR 来负责。
保证内部 API 跨版本的兼容性并不是此 JEP 的任务。这些 API 将依然保持不稳定状态,并很有可能在未经通知的情况下被更改。
动机
目前一些流行的第三方库正在使用非标准,不稳定,并且不受支持的 API,这些 API 实际上是 JDK 的内部实现细节,从未以交给外部调用作为目的来开发。在即将到来的模块化系统(JEP 200)中限制对这些 API 的访问将增进平台的完整性和安全性,因为许多此类内部 API 都涉及到特权以及安全敏感的操作。从长远看来,这一更改有助于降低 JDK/类库的维护者或应用程序在有意或无意地使用这些内部 API 时的成本。
描述
基于对多个大型代码仓库,包括 Maven 中心仓的分析,以及从 JDK8 以及它的依赖分析工具(jdeps)发布以后搜集到的反馈,我们可以将 JDK 的内部 API 分成以下两大类:
- 未被 JDK 以外的代码所调用的,或只是出于方便而被外部代码调用的 API,例如那些同时也被支持的 API 提供的,或由类库提供的功能(如 sun.misc.BASE64Decoder);以及
- 提供关键功能,这些功能如果不是不可能,那也是非常难以在 JDK 之外予以实现的 API (如 sun.misc.Unsafe)
在 JDK 9 中,我们提议:
- 默认封装所有非关键内部 API:定义他们的模块将不会把他们所在的包导出以供外部调用。(作为补救,可以通过编译时和运行时的命令行参数开启这些 API 的可访问性,直到这些 API 由于其它原因而被修改或者删除。)
- 封装在 JDK8 中有支持的替代 API 的关键内部 API,并且如上提供相应的补救方案。(所谓支持的替代 API,要么是 JDK 标准库的一部分,要么是 JDK 特定的,被 @jdk.Exported 所标注的代码(一般在 com.sun.* 或 jdk.* 包下面))
- 在 JDK8 中不存在替代 API 的关键内部 API 不予封装,并且把在 JDK9 中有支持的替代 API 的内部 API 标注为将要被封装,或者将要在 JDK10 中移除,不再建议使用(deprecate)。
建议在 JDK9 中保留可访问性的关键内部 API 如下:
- sun.misc.{Signal, SignalHandler}
- sun.misc.Unsafe(此类中许多方法的功能目前已可以在 variable handles (JEP 193)中找到)
- sun.reflect.Reflection::getCallerClass(int) (此方法的功能或许会由 JEP 259 作为标准形式提供出来)
- sun.reflect.ReflectionFactory.newConstructorForSerialization
欢迎根据现实使用场景以及对开发者与使用者造成的影响,对完善此列表提出任何建议。
以上关键内部 API 将会被放置于 JDK 特定的模块 jdk.unsupported 下,他们的包也会从这个模块中被导出。此模块会在完整的 JRE 和 JDK 镜像中被呈现。这些 API 也会默认可以被在类路径中的代码所访问,另外如果某个模块声明了针对 jdk.unsupported 的依赖,这个模块中的代码也能够访问这些 API。
正如上面提到的,在 JDK9 中有一些内部 API 的替代 API 已经部分或全部被实现了。开发者请尽可能在 JDK9 早期预览版本中测试这些替代 API,如果这些替代 API 不够全面,或有可以改进的地方,请尽早提供反馈。
在 JDK9 中引入了替代 API 的关键内部 API 将在 JDK9 中被标注为不再建议使用,并且在 JDK10 中被封装或移除。
导出 jdk.unsupported 和开放 sun.misc 与 sun.reflect 的后果如下:
- sun.misc 与 sun.reflect 中的非关键内部 API 将被移至他处,或如果适合的话,彻底移除,因为它们本不应能够被访问到。
- 标准模块和 JDK 模块不可以依赖于 jdk.unsupported。
调用了关键内部 API 的那些类库的维护者,如果这些关键内部 API 在 JDK9 中有替代 API,可以考虑使用多版本 JAR 包(JEP 238),以便发布可自动在 JDK9 下调用旧版 API,而在之后的版本调用替代 API 的产品。
风险与假设
如果在 JDK9 发布的时候,仍有一些被广泛调用的关键内部 API 尚未被发现,这些 API 的调方将会出错。针对这种情况的一个快速解决方案是最终用户使用上面提到的命令行标志将这些 API 暴露出来;而长期解决方案则是在 JDK9 的更新中把这些 API 移动到 jdk.unsupported 模块并导出供外部使用。
因为 jdk.unsupported 模块将会暴露 sun.misc 和 sun.reflect,这些模块内的所有非关键内部 API 将被移走或移除。如果移除,这些 API 将不再可见,或可访问。如果移走,他们可通过设置命令行标志而成为可访问的,但它们的全名将会被改变,例如,它们将会处在另一个名字不同的包下面。
除了前面提到的 sun.reflect 下的关键 API,此包中还包含了实现 java.lang(.reflect) 子系统的机制。这些机制将被移动到基础模块下一个内部的,非导出的包中。由此,将会导致反射调用的调用栈看上去和以前不同。具体而言,代表反射实现的栈帧的类名(StackTraceElement.getClassName())会从 sun.reflect.XXX 变成 jdk.internal.reflect.XXX。所有基于调用栈元素类名的代码分析或过滤,都需要进行恰当的更新以应对这一情况。详细细节参考 8137058 。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/99799.html