Java 什么是非法反射访问?

Java 什么是非法反射访问?,java,java-9,java-module,java-platform-module-system,Java,Java 9,Java Module,Java Platform Module System,Java 9中存在许多关于非法反射访问的问题 我发现了很多关于处理错误消息的讨论,但我很想知道什么是非法的反射访问 所以我的问题是: 什么定义了非法反射访问?什么情况触发了警告 我收集到它与Java 9中引入的封装原则有关,但我找不到一个解释,说明它是如何结合在一起的,是什么触发了警告,以及在什么情况下发生的。我发现了一个关于Java 9模块系统的Oracle 默认情况下,模块中的类型不能被其他模块访问,除非它是公共类型并且您导出了它的包。只公开要公开的包。对于Java9,这也适用于反射 正如中

Java 9中存在许多关于非法反射访问的问题

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

所以我的问题是:

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

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

我发现了一个关于Java 9模块系统的Oracle

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

正如中所指出的,JDK8和JDK9的
AccessibleObject#setAccessible
之间的差异具有指导意义。具体来说,JDK9添加了

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

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

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

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


现在这种方法需要更多的条件才能发挥作用。它没有破坏几乎所有旧软件的唯一原因是,从普通JAR自动生成的模块非常允许(为每个人打开并导出所有内容)。

除了了解模块之间的访问及其各自的包之外。我相信它的关键在于,我会挑选它的相关部分来尝试回答这个问题

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

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

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

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

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

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

这种封装的放松在运行时由一个新的启动程序选项
--非法访问
控制,在Java9中默认情况下,该选项等于
允许
许可
模式确保

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

这些模式可通过值
debug
(消息以及每次访问的stacktrace)、
warn
(每次访问的消息)和
deny
(禁用此类操作)进行配置


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

  • 使用
    --非法访问=deny
    运行它,了解并避免在没有模块声明(包括此类指令(
    打开
    )或明确使用
    --add opens
    VM arg)的情况下打开从一个模块到另一个模块的包
  • 可以使用带有
    --JDK internals
    选项的
    jdeps
    工具识别从编译代码到JDK内部api的静态引用
非法反射访问操作时发出的警告消息 检测到的数据具有以下形式:

WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM
其中:

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

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

有关此类示例警告的问题:=


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

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

java——列出模块| tr@“| awk'{print$1}”| xargs-n1 java-d

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

注意:使用JDK 11进行测试


重要提示:显然,如果要隐藏警告,请使用“-add opens”选项,这比包的提供程序不进行非法访问要好

第一个开放字符串Java11文档 您可以在其中找到模块和包名称

模块java.base,包java.lang

解决方案:

java --add-opens=java.base/java.lang=ALL-UNNAMED -jar example.jar
这可能会中断
java.lang.ClassLoader.findLoadedClass(java.lang.String)
java --add-opens=java.base/java.lang=ALL-UNNAMED -jar example.jar