什么是非法反光访问?

在Java 9中有很多关于非法反射访问的问题。

我发现有很多关于解决错误消息的讨论,但我很想知道什么是非法的反射访问。

所以我的问题是:

什么定义了非法反射访问,什么情况触发了警告?

我发现它与Java 9中引入的封装原则有关,但我找不到关于它如何结合在一起,什么触发警告以及在什么情况下的解释。


答案 1

除了了解模块及其各自软件包之间的访问。我相信它的症结在于模块系统#Relaxed-strong-encapsulation,我会挑选它的相关部分来尝试回答这个问题。

什么定义了非法反射访问,什么情况触发了警告?

为了帮助迁移到Java-9,可以放宽模块的强大封装。

  • 实现可以提供静态访问,即通过编译的字节码。

  • 可以提供一种调用其运行时系统的方法,其中一个或多个包的一个或多个模块对所有未命名模块中的代码开放,即在类路径上编码。如果以这种方式调用运行时系统,并且如果这样做,则反射 API 的某些调用会成功,否则它们将失败。

在这种情况下,您实际上最终进行了“非法”的反射访问,因为在纯模块化世界中,您不应该进行此类访问。

它是如何挂在一起的,在什么情况下触发警告?

这种封装的松弛在运行时由一个新的启动器选项控制,默认情况下,该选项在 Java9 中等于 。该模式确保--illegal-accesspermitpermit

对任何此类包的第一次反射访问操作都会导致发出警告,但在此之后不会发出任何警告。此单个警告描述如何启用进一步的警告。无法禁止显示此警告。

这些模式可使用值(每次此类访问的消息和堆栈跟踪)、(每个此类访问的消息)和(禁用此类操作)进行配置。debugwarndeny


在应用程序上进行调试和修复的几件事是: -

  • 运行它以了解并避免在没有模块声明(包括此类指令()或显式使用VM arg的情况下将包从一个模块打开到另一个模块。--illegal-access=denyopens--add-opens
  • 可以使用带有以下选项的工具识别从已编译代码到 JDK 内部 API 的静态引用jdeps--jdk-internals

检测到非法反射访问操作时发出的警告消息具有以下形式:

WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM

哪里:

$PERPETRATOR是包含调用相关反射操作的代码的类型加上代码源(即 JAR 文件路径)(如果可用)的完全限定名称,以及

$VICTIM是描述正在访问的成员的字符串,包括封闭类型的完全限定名称

此类示例警告的问题: = JDK9:发生了非法的反射访问操作。

最后,需要注意的是,在尝试确保您不会遇到此类警告并且将来安全的同时,您需要做的就是确保您的模块不会进行这些非法的反射访问。:)


答案 2

我发现有一篇关于Java 9模块系统的Oracle文章

默认情况下,其他模块无法访问模块中的某个类型,除非它是公共类型并且您导出其包。仅公开要公开的包。在Java 9中,这也适用于反射。

正如 https://stackoverflow.com/a/50251958/134894 中指出的那样,JDK8 和 JDK9 之间的差异很有启发性。具体来说,JDK9添加了AccessibleObject#setAccessible

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

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

它突出了模块及其导出的重要性(在Java 9中)


推荐