什么是非法反射访问

时间:2018-05-09 11:01:46

标签: java java-9 java-module

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

现在我无法找到,因为谷歌的所有人都在努力解决错误信息,实际上是非法的反射访问。

所以我的问题很简单:

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

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

5 个答案:

答案 0 :(得分:23)

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

  

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

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

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

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

在这种情况下,您实际上最终会进行反思访问 "非法" ,因为在纯粹的模块化世界中不打算进行此类访问。

  

如何将它们挂在一起以及触发警告的内容   场景吗

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

  

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

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

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

  • 使用--illegal-access=deny运行它以了解并避免打开包从一个模块到另一个模块而没有包含此类指令(opens)或模块声明的模块声明明确使用--add-opens VM arg。
  • 使用带有jdeps选项的--jdk-internals工具

    ,可以识别从已编译代码到JDK内部API的静态引用
      

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

    WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM
    
         

    其中:

         

    $PERPETRATOR是包含该类型的类型的完全限定名称   调用有问题的反射操作的代码加上代码   source(即JAR文件路径)(如果可用)和

         

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

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

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

答案 1 :(得分:14)

我找到了一个关于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中)

答案 2 :(得分:9)

只需查看用于访问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自动生成的模块非常宽松(打开并导出所有人的所有内容)。

答案 3 :(得分:1)

如果要使用add-open选项,请使用以下命令查找哪个模块提供了哪个软件包->

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

模块名称将与@一起显示,而软件包名称则不包含@

注意:已通过JDK 11测试

重要提示:显然比不对软件包提供者进行非法访问要好

答案 4 :(得分:1)

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