Java中的“非法访问操作”方法将被弃用吗?

5

JDK 9+ JVM会发出非法访问操作警告,如果您使用了一些非法访问,比如setAccessible()

我的问题

  1. setAccessible()在未来是否会被阻止?
  2. 如果这个特性被弃用,官方参考文献在哪里?

我找不到任何参考资料,谢谢提前。

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.hazelcast.internal.networking.nio.SelectorOptimizer (file:/var/folders/9w/wp9vfqmn2ql0mp3lgym0bxf40000gn/T/toy.war-spring-boot-libs-0024b388-730f-430b-b21b-1611bd2ad612/hazelcast-4.0.2.jar) to field sun.nio.ch.SelectorImpl.selectedKeys
WARNING: Please consider reporting this to the maintainers of com.hazelcast.internal.networking.nio.SelectorOptimizer
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

说某个功能将来会被移除意味着它现在已经被弃用了。https://dev59.com/0VUL5IYBdhLWcg3wf4J0#50251958 上有一些参考资料,虽然我不认为它们对未来是决定性的。 - dave_thompson_085
1个回答

9

1. setAccessible()在未来会被阻止吗?

不,AccessibleObject#setAccessible(boolean)既没有被弃用,也没有我知道的计划将其弃用。

您看到的警告与此方法相关,但并非直接相关。 Java 9中引入的Java平台模块系统在编译时和运行时(即反射)都增加了更强的封装性。 运行时规则由#setAccessible(boolean)记录:

如果以下任何一项成立,则调用类 C 可以使用此方法来启用对声明类 D 成员的访问权限:

  • CD 在同一个模块中。
  • 成员是公共的,D 在导出至至少包含 C 的模块的包中是公共的。
  • 成员是受保护的静态成员,D 在导出至至少包含 C 的模块的包中是公共的,且 CD 的子类。
  • D 在导出至至少包含 C 的模块的包中。所有未命名和开放模块中的所有包都对所有模块开放,因此当 D 在未命名或开放模块中时,此方法始终成功。

无法使用此方法来启用对私有成员、默认(包)访问权限的成员、受保护的实例成员或在声明类与调用者不在同一模块且包含声明类的包未向调用者的模块开放的受保护构造函数的访问权限。

这是一个重大变更,与Java 8不同的是,反射可以自由地访问它想要访问的任何内容(假设没有SecurityManager)。 这种破坏性的变化是一个问题,因为Java以向后兼容为傲。 为了给库和框架足够的时间进行迁移,他们放宽了特定情况下的强封装(见下文)。

2. 这个功能的官方参考文档(将来会被弃用)在哪里?

您看到的警告与--illegal-access选项有关,该选项在java工具规范中有记录:

当在运行时存在时,--illegal-access=需要一个关键字参数来指定操作模式:

注意: 该选项将在未来一个版本中被移除。

  • permit该模式打开运行时镜像中每个模块中的每个包以允许所有未命名模块(例如类路径上的代码)中的代码,如果该包存在于JDK 8中 [强调添加]。这使得通过平台的各种反射API进行静态访问(例如通过编译后的字节码和深度反射访问)成为可能。对任何此类包的第一个反射访问操作会导致发出警告。但是,在第一次出现后不会发出警告。这个单独的警告描述了如何启用进一步的警告。该模式是当前JDK的默认模式,但在未来版本中将更改 [强调添加]

  • warn:该模式与permit相同,只是对于每个非法反射访问操作都会发出警告消息。

  • debug:该模式与warn相同,只是对于每个非法反射访问操作都会发出警告消息和堆栈跟踪。

  • deny:该模式禁用所有非法访问操作,除了由其他命令行选项启用的操作,例如--add-opens在未来版本中,该模式将成为默认模式 [强调添加]

默认模式--illegal-access=permit旨在使您意识到类路径上的代码至少一次反射访问任何JDK内部API。要了解所有这些访问,可以使用warn或debug模式。对于需要非法访问的每个类库或框架,您有两个选择:

  • 如果组件的维护者已经发布了一个修复版本,不再使用JDK内部API,则可以考虑升级到该版本。

  • 如果组件仍需要修复,则可以联系其维护者,并要求他们将其使用JDK内部API替换为适当导出的API。

如果必须继续使用需要非法访问的组件,则可以使用一个或多个--add-opens选项来打开只需要访问的那些内部包,以消除警告消息。

为了验证应用程序已准备好使用JDK的未来版本,请运行--illegal-access=deny以及任何必要的--add-opens选项。任何剩余的非法访问错误很可能是由编译代码对JDK内部API的静态引用所致。您可以通过将jdeps工具与--jdk-internals选项一起运行来识别它们。出于性能原因,当前的JDK不会针对非法的静态访问操作发出警告。

总结强调部分:

  1. 默认模式为permit
    • 这允许在未命名模块(即类路径)中的代码访问运行时映像(即JDK)中模块内的成员,即使那些成员在非导出/非打开包中(只要这些包存在于JDK 8中)。
  2. 最终默认模式将变为deny
    • 此时没有经过适当迁移的任何代码将停止工作。这就是为什么您看到警告 - 他们希望您解决问题(如果是您自己的代码,则自己解决;如果是第三方代码,则提交错误报告)。
  3. --illegal-access选项本身最终将被完全删除。

这些更改将在哪个版本中发生...我不知道。然而,在默认模式变为deny之后,--illegal-access选项可能会在一两个版本之后被删除。

更新(2021年5月31日)

JEP 396: Strongly Encapsulate JDK Internals by Default已在Java 16中发布,并将默认模式设置为deny--illegal-access选项仍然存在。

JEP 403: Strongly Encapsulate JDK Internals目前针对Java 17进行了优化。这将使permitwarndebug模式失效,并且尝试使用它们将导致发出警告。但是,--illegal-access选项现在仍将保留(但计划在未来的版本中删除)。


答案可以视为 这个回答那个回答 的结合。 - Naman
@Naman 我应该删除这个答案吗? - Slaw
3
不完全是这样,它有两种好处都包含了。标记为重复是为了长期摆脱问答(QnA),如果保持原样的话。有加入信息到现有问题和对问题进行进一步编辑的条款,说明为什么不是重复等。因此,也有重新开放的机会,因此我建议保留它,除非在长期内有删除问答整体的行动。 - Naman
1
根据oracle.com/java/technologies/javase/16-relnotes.html,可以通过指定--illegal-access=permit使代码在JDK 16上运行。 - Sharm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接