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

我发现了大量关于如何处理错误消息的讨论,但我想知道非法反射访问到底是什么。

所以我的问题是:

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

我认为这与Java 9中引入的封装原则有关,但我找不到解释它是如何结合在一起的、什么触发警告以及在什么场景中触发警告的解释。


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

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

正如https://stackoverflow.com/a/50251958/134894中所指出的,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中)


除了理解模块和它们各自的包之间的访问之外。我相信它的关键在于模块系统#放松-强封装,我只会选择它的相关部分来尝试回答这个问题。

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

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

实现可以提供静态访问,即通过编译的字节码。 可以提供一种方法来调用其运行时系统,使其一个或多个模块的一个或多个包对所有未命名模块中的代码开放,即对类路径上的代码开放。如果以这种方式调用运行时系统,并且通过这样做,一些对反射api的调用将成功,否则它们将失败。

在这种情况下,你实际上已经结束了一个反射访问,这是“非法的”,因为在纯模块化的世界里,你不打算做这样的访问。

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

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

对任何此类包的第一个反射访问操作将导致 将发出警告,但此后不再发出警告。 这个警告说明了如何启用进一步的警告。这 警告不能被抑制。

模式可以通过以下值进行配置:debug(为每次这样的访问提供消息和stacktrace)、warn(为每次这样的访问提供消息)和deny(禁用这样的操作)。


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

使用——illegal-access=deny来运行它,以了解和避免在没有模块声明的情况下从一个模块打开包,包括这样的指令(open)或显式使用——add-open VM arg。 通过使用带有——jdk-internals选项的jdeps工具,可以识别已编译代码对jdk内部api的静态引用

非法反射访问操作时发出的警告消息 检测有以下形式: 警告:$犯罪者对$受害者的非法反射访问

地点: 的类型的完全限定名 调用有关反射操作的代码加上该代码 源(即jar文件路径),如果可用的话 $VICTIM是描述被访问成员的字符串, 包括封闭类型的完全限定名

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

最后,也是很重要的一点,在努力确保您不会面临这样的警告并确保将来安全的同时,您所需要做的就是确保您的模块没有进行这些非法的反射访问。:)


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

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

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

注意:使用JDK 11测试

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


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

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

例如,你有一个错误:

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

第一个开放字符串java 11文档 类字符串,可以在其中找到模块和包名

java模块。包java.lang

解决方案:

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