简体   繁体   English

什么是非法反射访问?

[英]What is an illegal reflective access?

There are a lot of questions about illegal reflective access in Java 9. Java 9 中有很多关于非法反射访问的问题。

I have found plenty of discussion about working around the error messages, but I would love to know what an illegal reflective access actually is.我发现了很多关于解决错误消息的讨论,但我很想知道非法反射访问实际上是什么。

So my question is:所以我的问题是:

What defines an illegal reflective access and what circumstances trigger the warning?什么定义了非法反射访问以及什么情况会触发警告?

I have gathered that it has something to do with the encapsulation principles that were introduced in Java 9, but I can't find an explanation of how it all hangs together, what triggers the warning, and in what scenario.我收集到它与 Java 9 中引入的封装原则有关,但我找不到关于它们如何结合在一起、触发警告的原因以及在什么情况下的解释。

Apart from an understanding of the accesses amongst modules and their respective packages.除了了解模块及其各自包之间的访问之外。 I believe the crux of it lies in the Module System#Relaxed-strong-encapsulation and I would just cherry-pick the relevant parts of it to try and answer the question.我相信它的症结在于模块系统#Relaxed-strong-encapsulation ,我只想挑选它的相关部分来尝试回答这个问题。

What defines an illegal reflective access and what circumstances trigger the warning?什么定义了非法反射访问以及什么情况会触发警告?

To aid in the migration to Java-9, the strong encapsulation of the modules could be relaxed.为了帮助迁移到 Java-9,可以放松模块的强封装。

  • An implementation may provide static access , ie by compiled bytecode.一个实现可以提供静态访问,即通过编译的字节码。

  • May provide a means to invoke its run-time system with one or more packages of one or more of its modules open to code in all unnamed modules , ie to code on the classpath.可以提供一种方法来调用其运行时系统,其中一个或多个模块的一个或多个包对所有未命名模块中的代码开放,即在类路径上进行编码。 If the run-time system is invoked in this way, and if by doing so some invocations of the reflection APIs succeed where otherwise they would have failed.如果以这种方式调用运行时系统,并且如果这样做,则反射 API 的某些调用会成功,否则它们将失败。

In such cases, you've actually ended up making a reflective access which is "illegal" since in a pure modular world you were not meant to do such accesses.在这种情况下,您实际上最终进行了“非法”反射访问,因为在纯模块化世界中,您并不打算进行此类访问。

How it all hangs together and what triggers the warning in what scenario?它是如何结合在一起的,什么情况下会触发警告?

This relaxation of the encapsulation is controlled at runtime by a new launcher option --illegal-access which by default in Java9 equals permit .这种封装的放松在运行时由一个新的启动器选项--illegal-access ,它在 Java9 中默认等于permit The permit mode ensures permit模式确保

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.无法抑制此警告。

The modes are configurable with values debug (message as well as stacktrace for every such access), warn (message for each such access), and deny (disables such operations).这些模式可以使用值debug (每个此类访问的消息以及堆栈跟踪)、 warn (每个此类访问的消息)和deny (禁用此类操作)进行配置。


Few things to debug and fix on applications would be:-调试和修复应用程序的几件事是:-

  • Run it with --illegal-access=deny to get to know about and avoid open ing packages from one module to another without a module declaration including such a directive( opens ) or explicit use of --add-opens VM arg.使用--illegal-access=deny运行它以了解并避免在没有模块声明的情况下从一个模块打开包到另一个模块( opens )或显式使用--add-opens VM arg。
  • Static references from compiled code to JDK-internal APIs could be identified using the jdeps tool with the --jdk-internals option可以使用带有--jdk-internals选项的jdeps工具识别从编译代码到 JDK 内部 API 的静态引用

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 (ie, JAR-file path), if available, and $PERPETRATOR是包含调用相关反射操作的代码以及代码源(即 JAR 文件路径)(如果可用)的类型的完全限定名称,以及

$VICTIM is a string that describes the member being accessed, including the fully-qualified name of the enclosing type $VICTIM是描述被访问成员的字符串,包括封闭类型的完全限定名称

Questions for such a sample warning: = JDK9: An illegal reflective access operation has occurred.此类示例警告的问题: = JDK9:发生了非法反射访问操作。 org.python.core.PySystemState org.python.core.PySystemState

Last and an important note, while trying to ensure that you do not face such warnings and are future safe, all you need to do is ensure your modules are not making those illegal reflective accesses.最后一个重要的注意事项,在努力确保您不会面临此类警告并且将来是安全的时,您需要做的就是确保您的模块不会进行那些非法的反射访问。 :) :)

There is an Oracle article I found regarding Java 9 module system我发现了一篇关于 Java 9 模块系统的 Oracle 文章

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.对于 Java 9,这也适用于反射。

As pointed out in https://stackoverflow.com/a/50251958/134894 , the differences between the AccessibleObject#setAccessible for JDK8 and JDK9 are instructive.正如https://stackoverflow.com/a/50251958/134894 中所指出的,JDK8 和 JDK9 的AccessibleObject#setAccessible之间的差异具有指导意义。 Specifically, JDK9 added具体来说,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 中的调用者可以使用此方法来启用对声明类 D 的成员的访问:

  • C and D are in the same module. C 和 D 在同一个模块中。
  • The member is public and D is public in a package that the module containing D exports to at least the module containing C.在包含 D 的模块导出到至少包含 C 的模块的包中,成员是公共的,D 是公共的。
  • 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.该成员是 protected static,D 在包含 D 的模块导出到至少包含 C 的模块的包中是公共的,并且 C 是 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. D 位于包含 D 的模块至少对包含 C 的模块开放的包中。未命名和开放模块中的所有包对所有模块开放,因此当 D 位于未命名或开放模块中时,此方法总是成功。

which highlights the significance of modules and their exports (in Java 9)它突出了模块及其导出的重要性(在 Java 9 中)

Just look at setAccessible() method used to access private fields and methods:只需查看用于访问private字段和方法的setAccessible()方法:

https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible-boolean- 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- https://docs.oracle.com/javase/9​​/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible-boolean-

Now there is a lot more conditions required for this method to work.现在,此方法需要更多条件才能起作用。 The only reason it doesn't break almost all of older software is that modules autogenerated from plain JARs are very permissive (open and export everything for everyone).它不会破坏几乎所有旧软件的唯一原因是从普通 JAR 自动生成的模块非常宽松(为每个人打开并导出所有内容)。

If you want to go with the add-open option, here's a command to find which module provides which package ->如果你想使用 add-open 选项,这里有一个命令来查找哪个模块提供了哪个包 ->

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

the name of the module will be shown with the @ while the name of the packages without it模块的名称将与@一起显示,而没有它的包的名称

NOTE: tested with JDK 11注意:使用 JDK 11 测试

IMPORTANT: obviously is better than the provider of the package does not do the illegal access重要提示:显然比包的提供者不做非法访问要好

if you want to hide warnings you could just use "--add-opens" option如果你想隐藏警告,你可以使用“--add-opens”选项

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

for example you have an error:例如你有一个错误:

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

First open String java 11 documentation Class String where you can find module and package name首先打开 String java 11 文档Class String在这里你可以找到模块和包名

Module java.base, Package java.lang模块 java.base,包 java.lang

Solution:解决方案:

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