【问题标题】:What is an illegal reflective access?什么是非法反射访问?
【发布时间】:2018-10-19 11:29:38
【问题描述】:

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

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

所以我的问题是:

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

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

【问题讨论】:

标签: java java-9 java-module java-platform-module-system


【解决方案1】:

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

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

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

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

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

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

这一切是如何联系在一起的,是什么触发了警告 场景?

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

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

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


在应用程序上调试和修复的事情很少:-

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

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

WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM

地点:

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

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

有关此类示例警告的问题:= JDK9: An illegal reflective access operation has occurred. org.python.core.PySystemState

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

【讨论】:

    【解决方案2】:

    我找到了一个关于 Java 9 模块系统的 Oracle article

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

    正如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 中)

    【讨论】:

    • 因此,如果我阅读了那篇文章,正确修改导出类中的私有属性是不可能的。只能修改受保护和公共属性。现在我不太关心 java 内部导出,但更关心 3rd 方库,我有时需要访问私有变量以将其设置为特定值。如果它把自己定义为一个模块,在这个方案中就不可能了,对吗?
    • 我没有直接的经验,但这是我的理解,并与其他地方提到的文章 (jaxenter.com/jdk-9-replace-permit-illegal-access-134180.html) 一起阅读似乎是这种情况。使用–illegal-access=permit启动你的JVM...
    • 嗯,这会让事情变得更有趣,当他们决定采用模块方式时,尝试让事情为某些事情工作。未来超级有趣。
    • 对于fun的各种值
    • 我接受了另一个答案,因为它提供了更多的解释并且更多的是对问题的回答,但遗憾的是我不能接受两个答案。
    【解决方案3】:

    看看setAccessible()方法用来访问private字段和方法:

    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 自动生成的模块非常宽松(为所有人打开和导出所有内容)。

    【讨论】:

      【解决方案4】:

      如果你想隐藏警告,你可以使用“--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
      

      【讨论】:

      • 这对我有用,非常感谢!!
      【解决方案5】:

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

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

      模块的名称将显示为@而包的名称不带它

      注意:使用 JDK 11 测试

      重要提示:明显优于包的提供者不做非法访问

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-03-22
        • 1970-01-01
        • 1970-01-01
        • 2021-04-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多