JDK16运行原有代码报java.lang.reflect.InaccessibleObjectException异常问询
异常场景说明
现有代码在JDK11环境运行完全正常,升级到JDK16环境后抛出如下异常:
java.lang.reflect.InaccessibleObjectException: Unable to make private static native long[] java.util.prefs.WindowsPreferences.WindowsRegOpenKey(long,byte[],int) accessible: module java.prefs does not "opens java.util.prefs" to unnamed module @3f803fae at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:357) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199) at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
此前代码从Java8迁移至Java9时,曾将WinRegistry.java中部分方法输入参数从int调整为long,该修改是为了适配64位系统下Windows注册表句柄的存储需求,属于符合JDK版本适配要求的调整,和本次JDK16下的异常没有直接关联。
异常产生原因
Java9引入了Java平台模块系统(JPMS),对JDK内部非公开API的反射访问做了分级限制:
- JDK9到JDK15版本处于过渡兼容阶段,未显式声明开放的内部包仍允许未命名模块(即未做模块化改造的普通Java应用)通过反射修改可访问性调用,仅会输出警告日志
- JDK16版本正式关闭了过渡兼容策略,默认禁止所有未在模块描述中显式声明
opens的JDK内部包被外部模块(包括未命名模块)反射访问
本次异常就是因为代码通过反射调用java.prefs模块下java.util.prefs包内的私有类WindowsPreferences的非公开方法,JDK16下java.prefs模块没有默认向未命名模块开放该包的反射访问权限,导致反射调用时权限校验失败。
解决方案
- 方案一:添加JVM启动参数临时开放权限
启动Java应用时添加如下启动参数即可直接解决问题,无需修改业务代码,适合快速修复生产问题:--add-opens java.prefs/java.util.prefs=ALL-UNNAMED
该参数的作用是显式将java.prefs模块下的java.util.prefs包向所有未命名模块开放反射访问权限,符合JPMS的权限规则。 - 方案二:替换JDK内部API调用,使用公开稳定的实现
反射调用JDK内部非公开API本身属于不推荐的实现方式,会随JDK版本迭代出现兼容性问题,建议替换为以下实现方案:- 基于Apache Commons Configuration的
WindowsRegistry工具类实现注册表操作,该工具类封装了稳定的公开API,不依赖JDK内部实现 - 通过JNA直接调用Windows系统原生
advapi32.dll中的注册表操作接口,完全脱离对JDK内部API的依赖
- 基于Apache Commons Configuration的
- 方案三:适配JPMS模块化改造
如果应用已经完成JPMS模块化改造,可以在项目的module-info.java中添加对应权限声明:
该方案适合长期跟进JDK版本迭代的模块化项目。open module your.module.name { requires java.prefs; }
内容的提问来源于stack exchange,提问作者Sangamesh_V
相关产品推荐
相关产品推荐

