module java.base does not “opens java.lang“ to unnamed module

在Java 9及以上版本运行应用程序时,在各种情况下都会发生此异常。
某些库和框架(Spring,Hibernate,JAXB)特别容易使用。
这是来自Javassist的示例:

java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @1941a8ff
    at java.base/jdk.internal.reflect.Reflection.throwInaccessibleObjectException(Reflection.java:427)
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:201)
    at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:192)
    at java.base/java.lang.reflect.Method.setAccessible(Method.java:186)
    at javassist.util.proxy.SecurityActions.setAccessible(SecurityActions.java:102)
    at javassist.util.proxy.FactoryHelper.toClass2(FactoryHelper.java:180)
    at javassist.util.proxy.FactoryHelper.toClass(FactoryHelper.java:163)
    at javassist.util.proxy.ProxyFactory.createClass3(ProxyFactory.java:501)
    at javassist.util.proxy.ProxyFactory.createClass2(ProxyFactory.java:486)
    at javassist.util.proxy.ProxyFactory.createClass1(ProxyFactory.java:422)
    at javassist.util.proxy.ProxyFactory.createClass(ProxyFactory.java:394) 

消息说:

无法使受保护的最终java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte [],int,int,java.security.ProtectionDomain)抛出java.lang.ClassFormatError可访问:模块java.base不会“打开java.lang”到未命名的模块@ 1941a8ff

为了避免异常并使程序成功运行,该怎么办?

参考方案

异常是由Java 9及以上版本中引入的Java Platform Module System引起的,特别是强封装的实现。
它仅在特定条件下允许access,最突出的条件是:

  • 类型必须是公共的
  • 必须导出拥有的软件包

对于反射,导致异常的代码尝试使用相同的限制。
更确切地说,异常是由对 setAccessible 的调用引起的。
在上面的堆栈跟踪中可以看到这一点,其中javassist.util.proxy.SecurityActions中的相应行如下所示:

static void setAccessible(final AccessibleObject ao,
                          final boolean accessible) {
    if (System.getSecurityManager() == null)
        ao.setAccessible(accessible); // <~ Dragons
    else {
        AccessController.doPrivileged(new PrivilegedAction() {
            public Object run() {
                ao.setAccessible(accessible);  // <~ moar Dragons
                return null;
            }
        });
    }
} 

为了确保程序成功运行,必须说服模块系统允许访问调用了setAccessible的元素。
所需的所有信息都包含在异常消息中,但是可以通过a number of mechanisms来实现。
哪一个最好,取决于导致它的确切情况。

无法使{member}可以访问:模块{A}不能向{B}打开{package}

到目前为止,最突出的方案是以下两种:

  • 库或框架使用反射来调用JDK模块。
    在这种情况下:
  • {A}是一个Java模块(以java.jdk.前缀)
  • {member}{package}是Java API

的一部分

  • {B}是一个库,框架或应用程序模块;经常unnamed module @...
  • 一个基于反射的库/框架,例如Spring,Hibernate,JAXB等,通过应用程序代码反射以访问bean,实体等。
    在这种情况下:
  • {A}是一个应用程序模块
  • {member}{package}是应用程序代码

的一部分

  • {B}是框架模块或unnamed module @...

请注意,某些库(例如JAXB)在两个帐户上都可能失败,因此请仔细查看您所处的场景!
问题中的一个是案例1。

1.反射调用JDK

JDK模块对于应用程序开发人员而言是不变的,因此我们无法更改其属性。
这仅留下一种可能的解决方案:command line flags。
有了它们,就有可能打开特定的包装以进行反思。

因此,在上述情况下(缩短)…

无法使java.lang.ClassLoader.defineClass可访问:模块java.base不会“打开java.lang”到未命名的模块@ 1941a8ff

…正确的解决方法是按以下方式启动JVM:

# --add-opens has the following syntax: {A}/{package}={B}
java --add-opens java.base/java.lang=ALL-UNNAMED 

如果反射代码在命名模块中,则ALL-UNNAMED可以替换为其名称。

请注意,有时可能很难找到一种方法将此标志应用于将实际执行反射代码的JVM。
如果所讨论的代码是项目构建过程的一部分,并且在构建工具产生的JVM中执行,则这可能会特别困难。

如果要添加的标志太多,则可以考虑使用encapsulation kill switch --permit-illegal-access代替。它将允许类路径上的所有代码反映所有已命名的模块。请注意,此标记**仅在Java 9 **中有效!

2.对应用程序代码的反思

在这种情况下,您很可能可以编辑反射被用来进入的模块。
(如果没有,则实际上是在情况1中。)这意味着不需要命令行标志,而是可以使用模块{A}的描述符打开其内部。
有多种选择:

  • 使用exports {package}导出软件包,这使得它在编译和运行时可用于所有代码
  • 使用exports {package} to {B}将包导出到访问模块,这使得它在编译和运行时可用,但仅对{B}

可用。

  • 使用opens {package}打开包,这使得它在运行时(带或不带反射)可用于所有代码
  • 使用opens {package} to {B}将包打开到访问模块,这使其在运行时可用(有或没有反射),但仅适用于{B}
  • 使用open module {A} { ... }打开整个模块,这将使其所有包在运行时(带有或不带有反射)可用于所有代码

升级JDK17出现:module java.base does not “opens java.lang.reflect“ to unnamed module

解决方法 增加运行参数

--add-opens java.base/java.lang.reflect=ALL-UNNAMED

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这个错误是因为Java 9及以上版本引入了模块化系统,而在模块化系统中,Java.lang包默认是对所有模块开放的,但是在这个错误中,Java.base模块没有将Java.lang包开放给未命名模块。这可能是因为您的代码中使用了Java 9及以上版本的特性,但是您的代码没有被编译为模块化的形式。要解决这个问题,您可以将您的代码编译为模块化的形式,或者在启动Java虚拟机时使用--add-opens选项将Java.lang包开放给未命名模块。 ### 回答2: 在Java 9中,引入了一个新的特性“模块化系统”,旨在解决Java应用程序之间依赖冲突的问题。模块是代码和资源的封装单元,可以明确定义导出和开放的包,以此来限制对其他模块的访问。 但是,在使用模块化系统时,我们可能会遇到以下错误信息:“module java.base does not 'opens java.lang' to unnamed module”,即Java基本模块不向未命名模块开放java.lang包。这个错误会发生在某些情况下,例如当我们使用反射机制或某些第三方库时,这些操作需要访问受限的包或类时。 问题的原因是Java基本模块(java.base)默认情况下仅向它使用的其他模块开放java.lang包,但未命名模块却无法访问它。未命名模块是指没有显式声明模块名称的类路径上的类集合。 解决这个问题的方法是显式地将java.lang包开放给我们使用的未命名模块。这可以在模块描述符(module-info.java)中添加以下声明来实现: open module mymodule { // ... opens java.lang; } 这里的mymodule是我们自己的模块名称,我们可以更改它以适应我们的应用程序模块描述符。 添加声明后,我们就可以使用反射机制或其他需要访问java.lang包的操作而不会遇到以上错误信息。 总的来说,在使用Java 9及更高版本的Java时遇到“module java.base does not 'opens java.lang' to unnamed module”问题是常见的,这是由于模块化系统的限制导致的。在解决此问题时,我们应该加强对模块描述符的理解,以便有针对性地进行干预。 ### 回答3: 这个错误信息的意思是java.base模块没有为未命名模块打开java.lang模块。在Java 9中,引入了模块系统,以避免类之间的冲突和不必要的类路径干扰。模块化应用程序将更易于管理和维护,模块之间的依赖性可以更清楚地表示。但是,这也带来了新的挑战。在模块化环境中,所有的Java代码都必须被分配到一个模块中,并且每个模块都必须声明其依赖项。 在这里,错误消息告诉我们java.base模块不会“打开”(也就是允许一个模块访问另一个模块的内部)java.lang模块。在Java 9中,java.lang模块被认为是可观察模块,并且它是自动打开的。但是,如果你在代码中使用反射或Hessian等技术访问java.lang包(例如通过分解字符串名称来实现),那么你需要显式地打开它。 为了解决这个问题,可以使用以下命令来打开java.lang模块: ``` --add-opens java.base/java.lang=ALL-UNNAMED ``` 这个命令不是在代码中使用的,而是在Java命令行中使用的。它告诉Java虚拟机打开java.base模块中的java.lang模块,并允许所有未命名模块访问它。如果你使用的是build tools,如Maven和Gradle,你可以在构建配置文件中添加这个命令。 总之,这个错误消息的原因是因为java.lang模块没有被打开到未命名模块,可以通过使用上述命令来解决。在实践中,了解模块化系统如何工作以及如何处理这些错误消息是非常重要的,以确保你的应用程序在Java 9及以上版本中能够正常运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值