什么是非法反思访问

关于Java 9中的非法reflection访问存在很多问题。

现在我找不到因为所有谷歌都在试图解决错误信息,实际上是非法的reflection访问。

所以我的问题很简单:

什么定义非法reflection访问以及什么情况触发警告?

我已经收集到它与Java 9中引入的封装原则有关,但是它们如何挂在一起以及什么触发警告在什么情况下我找不到解释。

除了了解模块及其各自包之间的访问之外。 我相信它的关键在于模块系统#Simply-strong-encapsulation ,我只是挑选它的相关部分来尝试回答这个问题。

什么定义非法reflection访问以及什么情况触发警告?

为了帮助迁移到Java9,可以放松模块的强大封装。

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

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

在这种情况下,您实际上最终会进行“非法”reflection访问 ,因为在纯粹的模块化世界中,您并不打算进行此类访问。

如何将它们挂在一起以及在什么情况下会触发警告?

这种封装的放宽在运行时由一个新的启动器选项--illegal-access ,默认情况下,Java9等于permitpermit模式确保

对任何此类包的第一次reflection访问操作会导致发出警告,但在该点之后不会发出警告。 此单个警告描述了如何启用进一步的警告。 无法抑制此警告。

模式可配置值debug (消息以及每个此类访问的堆栈跟踪), warn (每个此类访问的消息)和deny (禁用此类操作)。


调试和修复应用程序的几件事情是: –

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

    检测到非法reflection访问操作时发出的警告消息具有以下forms:

     WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM 

    哪里:

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

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

针对此类示例警告的问题:= JDK9:发生了非法reflection访问操作。 org.python.core.PySystemState

最后也是一个重要的注意事项,在尝试确保您不会面临此类警告并且未来安全的同时,您需要做的就是确保您的模块不会进行非法的reflection访问。 🙂

只需查看用于访问private字段和方法的setAccessible()方法:

https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible-boolean-

https://docs.oracle.com/javase/9​​/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible-boolean-

现在,此方法需要更多条件才能工作。 它几乎没有打破所有旧软件的唯一原因是从普通JAR自动生成的模块非常宽松(打开并导出所有人的一切)。

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

默认情况下,其他模块无法访问模块中的类型,除非它是公共类型并导出其包。 您只公开要公开的包。 使用Java 9,这也适用于reflection。

正如https://stackoverflow.com/a/50251958/134894中所指出的,JDK8和JDK9的AccessibleObject#setAccessible之间的区别是有益的。 具体来说,JDK9补充道

C类中的调用者可以使用此方法来访问声明类D的成员,如果以下任何一个持有:

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

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