You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

运行JUnit时Gson序列化Optional抛InaccessibleObjectException

异常产生原理

该异常是Java 9及以上版本引入的Java平台模块化系统(JPMS)强封装机制,结合旧版本Gson的序列化逻辑共同触发的:

  • 触发点就是日志行里的gson.toJson(roleDTOEmployee)调用:你传入的参数是Optional<RolesDTO>类型,2.8.6之前的老版本Gson没有为Optional类型实现专属序列化适配器,会默认走反射序列化逻辑。
  • Gson的反射序列化逻辑会调用setAccessible(true),尝试暴力访问Optional类的私有final字段value,拿到Optional内部存储的实际对象做序列化。
  • Java 9+的模块化规则明确要求:核心模块java.base默认不会向类路径上的未命名模块(也就是你运行JUnit测试时业务代码所在的加载模块)开放java.util包的深度反射访问权限,检测到非法反射访问时就会直接抛出InaccessibleObjectException。
  • 删除日志代码后,不会触发Gson序列化Optional实例的逻辑,自然不会触发非法反射访问,因此测试可以正常运行。
无需删除日志的解决方案

按推荐优先级排序:

  • 优先方案:升级Gson版本
    将项目使用的Gson依赖升级到2.8.6及以上正式版本,新版本已经内置了Optional、OptionalLong等JDK8新增类型的专属序列化适配器,序列化时不会再走反射访问私有字段的逻辑,从根源上规避模块访问限制,原有业务代码不需要做任何修改即可正常运行。
  • 兼容方案:序列化前手动解包Optional
    不直接将Optional实例传入Gson序列化,先手动取出Optional内部存储的实际值再做序列化,修改日志代码如下即可:
    if (logger.isInfoEnabled()) {
        // 用orElse取出内部值,空Optional时序列化为null
        logger.info("roleDTOEmployee {}", gson.toJson(roleDTOEmployee.orElse(null)));
    }
    
    这个方案不需要调整依赖、不需要加JVM参数,完全绕开了Gson对Optional类的反射操作,在各个JDK版本下都能稳定运行。
  • 临时方案:添加JVM启动参数放开反射限制
    如果暂时无法升级Gson、也不便于调整业务代码,可以在JUnit的运行配置中添加如下JVM启动参数,显式放开对应包的反射访问权限:
    --add-opens java.base/java.util=ALL-UNNAMED
    
    该参数会告知JVM允许所有未命名模块的代码通过反射访问java.util包下类的所有成员,直接绕过模块化的访问检查。注意这属于临时绕过方案,不推荐在生产环境长期使用。

内容的提问来源于stack exchange,提问作者Harsh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 14:51:18