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

GraalVM原生镜像中Jackson无法反序列化JsonNode为泛型ConcurrentHashMap

解决GraalVM原生镜像中Jackson反序列化ConcurrentHashMap的问题

问题背景

自定义Jackson反序列化器在JVM运行Jar包时完全正常,但通过GraalVM native-image工具转为原生镜像后,在convertValue调用处抛出异常:

java.lang.IllegalArgumentException: Cannot construct instance of `java.util.concurrent.ConcurrentHashMap` (no Creators, like default constructor, exist): no default constructor found
 at [Source: UNKNOWN; byte offset: #UNKNOWN]
        at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:4449)
        at com.fasterxml.jackson.databind.ObjectMapper.convertValue(ObjectMapper.java:4400)
        at myclient.sub.Subscriber$Deserializer.deserialize(Subscriber.java:78)
        at myclient.sub.Subscriber$Deserializer.deserialize(Subscriber.java:46)
        at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:4444)
        at com.fasterxml.jackson.databind.ObjectMapper.convertValue(ObjectMapper.java:4380)

尝试使用MapType替代TypeReference后问题仍存在,环境为Oracle GraalVM 21.0.2+13.1。

解决方案

1. 使用当前反序列化上下文的ObjectMapper

不要在反序列化器中新建ObjectMapper实例,而是复用当前上下文已配置好的ObjectMapper(通过jp.getCodec()获取),它会继承所有针对GraalVM原生镜像的适配配置:

// 替换原代码中(new ObjectMapper()).convertValue(...)的部分
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
accounts = mapper.convertValue(v, new TypeReference<ConcurrentHashMap<Integer, Account>>() {});

2. 显式配置ObjectMapper支持ConcurrentHashMap

如果必须新建ObjectMapper,需要显式注册ConcurrentHashMap的反序列化支持,确保GraalVM能识别其构造逻辑:

ObjectMapper mapper = new ObjectMapper();
// 注册ConcurrentHashMap的反序列化器
mapper.registerModule(new SimpleModule()
    .addDeserializer(ConcurrentHashMap.class, new MapDeserializer(
        mapper.getTypeFactory().constructMapType(ConcurrentHashMap.class, Integer.class, Account.class),
        null, null, null
    )));
accounts = mapper.convertValue(v, new TypeReference<ConcurrentHashMap<Integer, Account>>() {});

3. 添加GraalVM原生镜像配置文件

创建src/main/resources/META-INF/native-image/com.fasterxml.jackson.core/jackson-databind/reflect-config.json文件,告诉GraalVM保留ConcurrentHashMap的构造器元数据:

[
  {
    "name": "java.util.concurrent.ConcurrentHashMap",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true
  }
]

也可以添加Jackson官方的GraalVM支持依赖com.fasterxml.jackson.module:jackson-module-graal,它会自动生成所需的原生镜像配置。

4. 手动构建ConcurrentHashMap

绕过Jackson的反射转换逻辑,直接遍历JsonNode手动构建对象,彻底避免反射依赖问题:

if (v != null && v.isObject()) {
    accounts = new ConcurrentHashMap<>();
    ObjectMapper mapper = (ObjectMapper) jp.getCodec();
    Iterator<Map.Entry<String, JsonNode>> fields = v.fields();
    while (fields.hasNext()) {
        Map.Entry<String, JsonNode> entry = fields.next();
        Integer key = Integer.valueOf(entry.getKey());
        Account account = mapper.convertValue(entry.getValue(), Account.class);
        accounts.put(key, account);
    }
}

问题原因

GraalVM原生镜像是静态编译,会执行死代码消除和反射调用分析。Jackson在运行时通过反射创建ConcurrentHashMap,但GraalVM的静态分析未检测到该反射路径,因此未生成对应的构造器元数据,导致运行时无法找到构造方法。而JVM环境是动态执行,反射可以正常访问类的构造器。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 03:35:40