什么是非法反射访问?

What is an illegal reflective access?

Java9.

里面有很多关于非法反射访问的问题

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

所以我的问题是:

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

我了解到它与 Java 9 中介绍的封装原则有关,但我找不到关于它们如何挂在一起、触发警告的原因以及在什么情况下。

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

By default, a type in a module is not accessible to other modules unless it’s a public type and you export its package. You expose only the packages you want to expose. With Java 9, this also applies to reflection.

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

This method may be used by a caller in class C to enable access to a member of declaring class D if any of the following hold:

  • C and D are in the same module.
  • The member is public and D is public in a package that the module containing D exports to at least the module containing C.
  • The member is protected static, D is public in a package that the module containing D exports to at least the module containing C, and C is a subclass of D.
  • D is in a package that the module containing D opens to at least the module containing C. All packages in unnamed and open modules are open to all modules and so this method always succeeds when D is in an unnamed or open module.

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

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

What defines an illegal reflective access and what circumstances trigger the warning?

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

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

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

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

How it all hangs together and what triggers the warning in what scenario?

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

The first reflective-access operation to any such package causes a warning to be issued, but no warnings are issued after that point. This single warning describes how to enable further warnings. This warning cannot be suppressed.

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


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

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

The warning message issued when an illegal reflective-access operation is detected has the following form:

WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM

where:

$PERPETRATOR is the fully-qualified name of the type containing the code that invoked the reflective operation in question plus the code source (i.e., JAR-file path), if available, and

$VICTIM is a string that describes the member being accessed, including the fully-qualified name of the enclosing type

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

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

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

java --list-modules | tr @ " " | awk '{ print }' | 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 java11文档 Class String 在哪里可以找到模块和包名称

模块 java.base,包 java.lang

解决方案:

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