在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}
到目前为止,最突出的方案是以下两种:
在这种情况下:
{A}
是一个Java模块(以java.
或jdk.
前缀){member}
和{package}
是Java API 的一部分
{B}
是一个库,框架或应用程序模块;经常unnamed module @...
在这种情况下:
{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} { ... }
打开整个模块,这将使其所有包在运行时(带有或不带有反射)可用于所有代码有关这些方法的更详细的讨论和比较,请参见this post。
Java-如何将此字符串转换为日期? - java我从服务器收到此消息,我不明白T和Z的含义,2012-08-24T09:59:59Z将此字符串转换为Date对象的正确SimpleDateFormat模式是什么? java大神给出的解决方案 这是ISO 8601标准。您可以使用SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM…
为什么`if(guess!='a'|| guess!='A'||…)`不起作用? - javaImprove this question 这是我的代码,我知道if语句真的很长,代码可能会更高效,但是我只是想知道答案,因为它使我发疯。while (whileloop == 1) { if (guess != 'a' || guess != 'A' || guess != 'b' || gues…
输入URL字段时,出现错误“远程URL测试失败:不支持协议'git clone HTTPS'” - java当我单击“定义远程”时,输入在Android Studio的Bitbucket站点中提供的HTTPS URL时,出现错误远程URL测试失败:不支持协议'git clone https'我使用了SSH URL,这给了我错误远程URL测试失败:警告:将IP地址“ 18.205.93.0”的RSA主机密钥永久添加到已知主机列表中。无法从远程存储库读取。我什至尝试从…
运行jar的python子进程给出错误 - java在终端中运行此命令可以正常工作:java -jar file.jar --arg1 --arg2 pathTofile 然后,当我尝试使用以下代码从python代码执行此操作时:subprocess.call(['java', '-jar', 'file.jar', '--arg1…
获取字符的unicode值 - java有什么方法可以获取字符的键码吗?例如getKeycode('C'); 有没有类似的东西?谢谢 参考方案 char ch='c'; int code = ch; System.out.println(code); 输出:99 只是为了转义char \,您必须像char ch='\\';一样使用