【问题标题】:How to use sun.reflect package in jdk9/java-9?如何在 jdk9/java-9 中使用 sun.reflect 包?
【发布时间】:2017-06-08 18:39:02
【问题描述】:

我正在使用 jdk-9,我想在我的代码中使用 sun.reflect.* 包,但我收到以下异常

Exception in thread 'main' java.lang.IllegalAccessError : class Test (in moudle: Unnamed Module) cannot access class sun.reflect.Reflaction (in module:java.base), sun.reflect is not exported to Unnamed module

当我使用 JDK-9 运行下面的示例代码时

public static void main(String args[]){
   System.out.println(Reflection.getCallerClass(3));
}

【问题讨论】:

    标签: java java-9 java-platform-module-system


    【解决方案1】:

    这些 sun.* 包从来都不是官方 API 的一部分,也不能保证存在,即使在 Java 9 之前的 JVM 中也是如此。做好准备,让它们在未来完全消失,甚至无法通过某些选项恢复。值得庆幸的是,有一个涵盖此功能的官方 API,无需使用非官方 API。

    获取直接调用者类
    Class<?> c = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
                            .getCallerClass();
    
    获取堆栈上的第 n 个调用者(例如,第三个,如您的示例中):
    Class<?> c = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).walk(s ->
         s.map(StackWalker.StackFrame::getDeclaringClass).skip(3).findFirst().orElse(null));
    

    【讨论】:

    • 这在 JDK10 之后有效,支持可见性作为正确答案。
    【解决方案2】:

    适用于较新的 OpenJDK 9 EA 版本。例如:

    $ java -version
    java version "9-ea"
    Java(TM) SE Runtime Environment (build 9-ea+138)
    Java HotSpot(TM) 64-Bit Server VM (build 9-ea+138, mixed mode)
    
    $ javac Test.java
    Test.java:5: warning: Reflection is internal proprietary API and may be removed in a future release
        System.out.println(Reflection.getCallerClass(3));
                           ^
    Note: Test.java uses or overrides a deprecated API.
    Note: Recompile with -Xlint:deprecation for details.
    1 warning
    
    $ java Test
    null
    

    似乎它作为JDK-8137058 的一部分在 9-ea+115 版本中得到了修复。因此,您可能正在使用较旧的 EA 版本。总的来说,@Holger 是对的:这个 API 很有可能在未来的 Java 版本中完全消失,所以考虑迁移到 StackWalker API。

    【讨论】:

    • 确实,sun.reflect.Reflection 已移至 jdk.unsupported 模块。所以它至少会在 Java 10 之前存在。
    【解决方案3】:

    此答案已过时 - 请检查 THIS ONE INSTEAD!

    模块系统的一个特点是,由于新的accessibility rules,它允许库开发人员强烈封装实现细节。简而言之,sun.*com.sun.* 包中的大多数类型将不再可访问。这与 Sun 和后来的 Oracle 声明这些软件包不供公众使用是一致的。

    一种解决方法是在编译和启动时使用命令行标志导出这些包:

    --add-exports java.base/sun.reflect=ALL-UNNAMED
    

    这会将包sun.reflect从模块java.base导出到所有模块,包括unnamed module,它是收集类路径上所有类的那个。

    【讨论】:

      【解决方案4】:
      java -cp classes -XaddExports:java.base/sun.reflect Test
      

      Jigsaw (java-9) 具有模块化概念,他们为 compact-1 设计了 ​​java.base 包,并封装了sun.reflect.*。所以sun.reflect.* 无法在外面访问。 由于这个原因,它给出了例外

      Exception in thread 'main' java.lang.IllegalAccessError : class Test (in moudle: Unnamed Module) cannot access class sun.reflect.Reflaction (in module:java.base), sun.reflect is not exported to Unnamed module
      

      为了提供向后兼容性,他们提供了使用该软件包的方法,如下所示。

      java -cp classes -XaddExports:java.base/sun.reflect Test
      

      【讨论】:

      • 答案有些误导:(1)紧凑型配置文件与它无关; (2) 其他各种包也失败; (3) 错误信息和建议的解决方案已过时。请参阅my answer
      • @Nicolai 你的回答已经过时了。请参阅this answer :)
      【解决方案5】:

      使用Migration documentation 中提到的最新版本和引入的更改更新线程。不过,pointed out by @Holger 已经很正确了。

      在 JDK 9 中仍可访问的 sun.reflect 包中的 API 是:

      • sun.reflect.Reflection::getCallerClass(int) 相反,使用 堆栈遍历 API,请参阅 JEP 259: Stack-Walking API.
      • sun.reflect.ReflectionFactory.newConstructorForSerialization

      这些 API 在运行时默认是可访问的。它们已移至 JRE 和 JDK 映像中的 jdk.unsupported 模块。需要这些 API 的模块必须声明对 jdk.unsupported 模块的依赖。

      sun.misc 和 sun.reflect 包中的其余内部 API 已被移动,因为它们不应该被访问。如果您需要使用这些内部 API 之一,您可以使用 --add-exports 命令行选项打破封装。 (类似于answered by @NIrav)。 尽管如文档中所建议的那样,此选项应仅用作迁移的临时帮助。

      【讨论】:

      • 如果您是 java9 模块的新手,知道您必须在包中添加一个名为 module-info.java 的新文件来声明对 jdk.unsupported 的依赖关系会很有用。它看起来像这样:module com.mypackage { requires jdk.unsupported; requires org.apache.logging.log4j; exports com.mypackage; }
      猜你喜欢
      • 1970-01-01
      • 2020-01-26
      • 1970-01-01
      • 1970-01-01
      • 2020-02-21
      • 1970-01-01
      • 1970-01-01
      • 2016-04-29
      • 2017-08-11
      相关资源
      最近更新 更多