升级JDK1.8 到 JDK17 时访问报Unable to make protected native ??? .clone() 的异常 | 您所在的位置:网站首页 › unnamed中文意思 › 升级JDK1.8 到 JDK17 时访问报Unable to make protected native ??? .clone() 的异常 |
问题重现
异常代码
2023-06-05T23:35:32.328+08:00 ERROR 6968 --- [nio-8080-exec-9] y.s.c.exception.ExceptionInterception : Unable to make protected native java.lang.Object java.lang.Object.clone() throws java.lang.CloneNotSupportedException accessible: module java.base does not "opens java.lang" to unnamed module @1757cd72
java.lang.reflect.InaccessibleObjectException: Unable to make protected native java.lang.Object java.lang.Object.clone() throws java.lang.CloneNotSupportedException accessible: module java.base does not "opens java.lang" to unnamed module @1757cd72
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) ~[na:na]
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) ~[na:na]
at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199) ~[na:na]
at java.base/java.lang.reflect.Method.setAccessible(Method.java:193) ~[na:na]
现在还有很长,不放了,大概意思就是这样 问题重点及分析module java.base does not "opens java.lang" to unnamed module 翻译得:base 模块中没将 lang 模块打包到未命名模块。 说人话,人家不想给我们用 clone() 了,人家把 accessiable 属性设置为 false 了。 在 JDK1.8 及之前,这个属性是打开的,意思就是 1.8 前会反射出里面的所有方法。我们都知道反射需要大量的性能。然而这些方法的使用频率不高,为了这些调用可能性低的代码牺牲这些性能显然不值,何况有性能更高的方法来代替了这些方法,所以从 JDK1.8 后大量包的可访问反射属性都被关闭了,lang 就是其中之一。 这也就是很多 Java 工程以前明明用的好好的,升级后会报大量异常的原因。 问题证明声明:出问题的异常的版本为 JDK17。 异常第二行中点击进去跳转到 AccessibleObject 的第 354 行,方法如下。 仔细分析会发现,在最后一个判断前,可设置访问属性全部都是返回true。 当上面都返回 false,第三个参数又为 true ,此时一定会报异常。 private boolean checkCanSetAccessible(Class caller, Class declaringClass, boolean throwExceptionIfDenied) { if (caller == MethodHandle.class) { throw new IllegalCallerException(); // should not happen } Module callerModule = caller.getModule(); Module declaringModule = declaringClass.getModule(); if (callerModule == declaringModule) return true; if (callerModule == Object.class.getModule()) return true; if (!declaringModule.isNamed()) return true; String pn = declaringClass.getPackageName(); int modifiers; if (this instanceof Executable) { modifiers = ((Executable) this).getModifiers(); } else { modifiers = ((Field) this).getModifiers(); } // class is public and package is exported to caller boolean isClassPublic = Modifier.isPublic(declaringClass.getModifiers()); if (isClassPublic && declaringModule.isExported(pn, callerModule)) { // member is public if (Modifier.isPublic(modifiers)) { return true; } // member is protected-static if (Modifier.isProtected(modifiers) && Modifier.isStatic(modifiers) && isSubclassOf(caller, declaringClass)) { return true; } } // package is open to caller if (declaringModule.isOpen(pn, callerModule)) { return true; } if (throwExceptionIfDenied) { // not accessible String msg = "Unable to make "; if (this instanceof Field) msg += "field "; msg += this + " accessible: " + declaringModule + " does not \""; if (isClassPublic && Modifier.isPublic(modifiers)) msg += "exports"; else msg += "opens"; msg += " " + pn + "\" to " + callerModule; InaccessibleObjectException e = new InaccessibleObjectException(msg); if (printStackTraceWhenAccessFails()) { e.printStackTrace(System.err); } throw e; } return false; }再看异常提示:module java.base does not "opens java.lang" to unnamed module 关于 module 的。 在方法里我们发现在 339 行判断了 module ,刚好那个检查的方法就叫 isOpen ,显然检测访问属性的这里。 通过打断点可以发现,Module 的 560 行调用了616 行的方法,很不幸所有能返回 true 的一个没踩到。 到此,完全可以证明 lang 包不被开放可访问属性。 解决方法既然知道了问题,那简单,让他放开就可以了。但这个明显是关于 JVM 的问题,所以我们需要去 JVM 里调参。那简单点启动时给他加上这个开放属性就可以了。 具体如下 1 2 3 代码在这里 –add-opens java.base/java.lang=ALL-UNNAMED 至此,这个问题已解决。再测试,clone() 方法已经正常用了。 总结一个问题是由多方位造成的,写了这么多不仅仅只是想让大家知道这个问题如何解决,更想传递一种定位问题、解决问题的方法。 行成于思,善于思考才是最重要的!! END |
CopyRight 2018-2019 实验室设备网 版权所有 |