繁体   English   中英

什么是非法反射访问?

[英]What is an illegal reflective access?

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

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

所以我的问题是:

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

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

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

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

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

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

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

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

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

这种封装的放松在运行时由一个新的启动器选项--illegal-access ,它在 Java9 中默认等于permit permit模式确保

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

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


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

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

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

 WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM

在哪里:

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

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

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

最后一个重要的注意事项,在努力确保您不会面临此类警告并且将来是安全的时,您需要做的就是确保您的模块不会进行那些非法的反射访问。 :)

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

默认情况下,模块中的类型不能被其他模块访问,除非它是公共类型并且您导出它的包。 您只公开要公开的包。 对于 Java 9,这也适用于反射。

正如https://stackoverflow.com/a/50251958/134894 中所指出的,JDK8 和 JDK9 的AccessibleObject#setAccessible之间的差异具有指导意义。 具体来说,JDK9添加了

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

  • C 和 D 在同一个模块中。
  • 在包含 D 的模块导出到至少包含 C 的模块的包中,成员是公共的,D 是公共的。
  • 该成员是 protected static,D 在包含 D 的模块导出到至少包含 C 的模块的包中是公共的,并且 C 是 D 的子类。
  • D 位于包含 D 的模块至少对包含 C 的模块开放的包中。未命名和开放模块中的所有包对所有模块开放,因此当 D 位于未命名或开放模块中时,此方法总是成功。

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

只需查看用于访问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 自动生成的模块非常宽松(为每个人打开并导出所有内容)。

如果你想使用 add-open 选项,这里有一个命令来查找哪个模块提供了哪个包 ->

java --list-modules | tr @ " " | awk '{ print $1 }' | xargs -n1 java -d

模块的名称将与@一起显示,而没有它的包的名称

注意:使用 JDK 11 测试

重要提示:显然比包的提供者不做非法访问要好

如果你想隐藏警告,你可以使用“--add-opens”选项

--add-opens <source-module>/<package>=<target-module>(,<target-module>)*

例如你有一个错误:

java.lang.ClassLoader.findLoadedClass(java.lang.String)

首先打开 String java 11 文档Class String在这里你可以找到模块和包名

模块 java.base,包 java.lang

解决方案:

java --add-opens=java.base/java.lang=ALL-UNNAMED -jar example.jar

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM