升级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

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值