Nic*_*lai 52 java reflection java-9
在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)
Run Code Online (Sandbox Code Playgroud)
消息说:
无法使受保护的最终java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte [],int,int,java.security.ProtectionDomain)抛出java.lang.ClassFormatError可访问:module java.base没有"打开java.lang"到未命名的模块@ 1941a8ff
可以做些什么来避免异常并使程序成功运行?
Nic*_*lai 81
例外是由Java 9中引入的Java平台模块系统引起的,特别是它强大的封装实现.它只允许在特定条件下访问,最突出的是:
对于反射也是如此,导致异常的代码试图使用.更准确地说,异常是由调用引起的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;
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
为了确保程序成功运行,必须说服模块系统允许访问setAccessible被调用的元素.所需的所有信息都包含在异常消息中,但有许多机制可以实现这一点.哪个是最好的取决于导致它的确切场景.
无法访问{member}:模块{A}没有'打开{package}'到{B}
到目前为止,最突出的情况是以下两种情况:
库或框架使用反射来调用JDK模块.在这种情况下:
{A}是一个Java模块(以java.或为前缀jdk.){member}并且{package}是Java API的一部分{B}是一个库,框架或应用程序模块; 经常unnamed module @...一个基于反射的库/框架,如Spring,Hibernate,JAXB,......反映了应用程序代码访问bean,实体,....在这种情况下:
{A} 是一个应用程序模块{member}并且{package}是应用程序代码的一部分{B} 是框架模块或 unnamed module @...请注意,某些库(例如JAXB)可能在两个帐户上都失败,因此请仔细查看您所处的情况!问题中的一个是案例1.
JDK模块对于应用程序开发人员来说是不可变的,因此我们无法更改其属性.这只留下一种可能的解决方案:命令行标志.使用它们可以打开特定的包进行反射.
所以在上面这样的情况下(缩短)......
无法使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
Run Code Online (Sandbox Code Playgroud)
如果反射代码在命名模块中,ALL-UNNAMED则可以用其名称替换.
请注意,有时很难找到将此标志应用于实际执行反射代码的JVM的方法.如果有问题的代码是项目构建过程的一部分并且在构建工具生成的JVM中执行,那么这可能会特别困难.
如果要添加的标志太多,您可以考虑使用封装kill开关 --permit-illegal-access.它将允许类路径上的所有代码反映所有命名模块.请注意,此标志 仅适用于Java 9!
在这种情况下,您可能可以编辑反射用于闯入的模块.(如果不是,那么你的实际情况就是1.)这意味着命令行标志不是必需的,而是模块{A}的描述符可以用来打开它的内部.有多种选择:
exports {package},使其在编译和运行时可用于所有代码exports {package} to {B},这使得它在编译和运行时可用,但仅限于{B}opens {package},使其在运行时(有或没有反射)可用于所有代码opens {package} to {B},使其在运行时(有或没有反射)可用,但仅限于{B}open module {A} { ... },这使得所有包在运行时(有或没有反射)可用于所有代码有关这些方法的更详细讨论和比较,请参阅此文章.
Pan*_*kos 37
只是最近的反馈
许多解决此问题的建议都与虚拟机启动器选项有关--illegal-access。
据 Oracle 称,随着JEP 403 (link1)和JEP 403 (link2)已决定从 JDK 17 及更高版本发布,启动器选项--illegal-access将停止工作!
总结 严格封装 JDK 的所有内部元素,除了关键的内部 API(例如 sun.misc.Unsafe)。将不再可能通过单个命令行选项来放松内部元素的强封装,这在 JDK 9 到 JDK 16 中是可能的。
和
通过此更改,最终用户将无法再使用 --illegal-access 选项来启用对 JDK 内部元素的访问。(受影响的包的列表可在此处找到。)sun.misc 和 sun.reflect 包仍将由 jdk.unsupported 模块导出,并且仍将打开,以便代码可以通过反射访问其非公共元素。不会以这种方式打开其他 JDK 包。
仍然可以使用--add-opens 命令行选项或 Add-Opens JAR 文件清单属性来打开特定包。
所以以下解决方案将继续有效
# --add-opens has the following syntax: {A}/{package}={B}
java --add-opens java.base/java.lang=ALL-UNNAMED
Run Code Online (Sandbox Code Playgroud)
但解决方案--illegal-access将停止工作JDK 17。
小智 25
仍然可以尝试使用旧的 JKD 版本。
对于 Eclipse 你需要做两件事。去
窗口 -> 首选项 -> java -> 编译器 将编译器合规级别设置为特定版本 在我的例子中,Eclipse 版本设置为 JDK 16 我将其恢复为 1.8,因为我的代码是用 1.8 编写的
窗口 -> 首选项 -> Java -> 安装的 JRE。 添加JRE安装路径(选择Standard VM)
它对我来说很顺利..
Java模块化后,有些jdk内部类无法访问,就会直接报错。因此,在执行Java程序时,您需要在VM配置中添加以下选项:
对于 Intellij 用户,请执行以下步骤:编辑运行/调试配置 -> 在 VM 选项中添加以下选项:
--add-opens java.base/java.lang=ALL-UNNAMED
Run Code Online (Sandbox Code Playgroud)
此链接可能会有所帮助:升级到 Java 17
小智 7
添加到 JVM 命令行或转到eclipse.iniEclipse 目录中的文件并添加以下内容:
--add-opens java.base/java.lang=ALL-UNNAMED
Run Code Online (Sandbox Code Playgroud)
Java 17 之前的版本(因为 Java 17 删除了此参数)您还需要添加:
--illegal-access=warn
Run Code Online (Sandbox Code Playgroud)
使用 --add-opens 应该被视为一种解决方法。正确的做法是让 Spring、Hibernate 和其他图书馆通过非法访问来解决他们的问题。
小智 5
这是一个非常具有挑战性的问题。正如其他人所指出的, --add-opens 选项只是一种解决方法。一旦 Java 9 公开可用,解决潜在问题的紧迫性只会增加。
在 Java 9 上测试我的基于 Hibernate 的应用程序时收到这个确切的 Javassist 错误后,我发现自己在这个页面上。由于我的目标是在多个平台上支持 Java 7、8 和 9,我努力寻找最佳解决方案。(请注意,Java 7 和 8 JVM 在命令行上看到无法识别的“--add-opens”参数时将立即中止;因此无法通过对批处理文件、脚本或快捷方式进行静态更改来解决此问题。)
从主流库(例如 Spring 和 Hibernate)的作者那里得到官方指导会很高兴,但是距离当前预计的 Java 9 发布还有 100 天,这个建议似乎仍然很难找到。
经过大量的实验和测试,我终于找到了 Hibernate 的解决方案:
这避免了 Hibernate 在运行时执行基于 Javassist 的类修改的需要,消除了原始帖子中显示的堆栈跟踪。
但是,您应该在之后彻底测试您的应用程序。Hibernate 在构建时应用的字节码更改似乎与运行时应用的不同,导致应用程序行为略有不同。当我启用构建时字节码增强时,我的应用程序中已经成功多年的单元测试突然失败了。(我不得不追查新的 LazyInitializationExceptions 和其他问题。)而且行为似乎因 Hibernate 的一个版本而异。谨慎行事。
小智 5
我在 2021 年使用 openJDK 1.8 和 STS 4 时遇到了同样的问题。
Window => Preferences => Java => Installed JREs.
Run Code Online (Sandbox Code Playgroud)
我使用添加选项添加了一个新的 JRE(如下所述),浏览到 openJdk 文件夹,选择“确定”。将新的 JDK 设置为默认值。单击应用并关闭。
/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre
Run Code Online (Sandbox Code Playgroud)
它就像一个魅力:)
| 归档时间: |
|
| 查看次数: |
17195 次 |
| 最近记录: |