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

Spring Data Mongo保存含Class<?>属性文档时CodecConfigurationException异常

解决方案:Spring Data MongoDB处理Class<?>类型编解码异常

问题根源

异常CodecConfigurationException: Can't find a codec for CodecCacheKey{clazz=class java.lang.Class, types=null}的核心原因是MongoDB驱动没有默认的Class<?>类型编解码器,同时你之前的转换器注册方式存在错误,导致自定义转换器未生效。


方案一:全局处理Class<?>类型编解码

直接为Class类型编写双向转换器,所有涉及Class属性的实体都能自动适配。

1. 编写Class与String的双向转换器

// 存储时:Class转为全类名字符串
public class ClassToStringConverter implements Converter<Class<?>, String> {
    @Override
    public String convert(Class<?> source) {
        return source.getName();
    }
}

// 读取时:字符串转为Class实例
public class StringToClassConverter implements Converter<String, Class<?>> {
    @Override
    public Class<?> convert(String source) {
        try {
            return Class.forName(source);
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("无法加载指定类: " + source, e);
        }
    }
}

2. 正确注册转换器

修正之前错误的注册逻辑,使用MongoCustomConversions.create()回调方式完成配置:

@Bean
public MongoCustomConversions customConversions() {
    List<Converter<?, ?>> converters = new ArrayList<>(List.of(
        // 保留原有时间转换器
        JSR310DateConverters.ZonedDateTimeToDateConverter.INSTANCE,
        JSR310DateConverters.DateToZonedDateTimeConverter.INSTANCE,
        JSR310DateConverters.LongToDurationConverter.INSTANCE,
        JSR310DateConverters.DurationToLongConverter.INSTANCE,
        // 添加Class类型转换器
        new ClassToStringConverter(),
        new StringToClassConverter()
    ));

    return MongoCustomConversions.create(adapter -> {
        adapter.useNativeDriverJavaTimeCodecs();
        adapter.registerConverters(converters);
    });
}

方案二:针对JobParameter单独编写转换器

如果只想针对JobParameter类处理,可编写该类与MongoDBDocument的双向转换器。

1. 编写JobParameter的双向转换器

// 存储时:JobParameter转为Document
public class JobParameterToDocumentConverter implements Converter<JobParameter<?>, Document> {
    @Override
    public Document convert(JobParameter<?> source) {
        Document doc = new Document();
        doc.put("value", source.getValue());
        doc.put("type", source.getType().getName()); // 存储类的全路径名
        doc.put("identifying", source.isIdentifying());
        return doc;
    }
}

// 读取时:Document转为JobParameter
public class DocumentToJobParameterConverter implements Converter<Document, JobParameter<?>> {
    @SuppressWarnings("unchecked")
    @Override
    public JobParameter<?> convert(Document source) {
        JobParameter<?> param = new JobParameter<>();
        param.setValue(source.get("value"));
        
        try {
            Class<?> type = Class.forName(source.getString("type"));
            param.setType((Class<Object>) type);
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("无法加载JobParameter指定的类", e);
        }
        
        param.setIdentifying(source.getBoolean("identifying"));
        return param;
    }
}

2. 注册JobParameter转换器

同样使用正确的注册方式:

@Bean
public MongoCustomConversions customConversions() {
    List<Converter<?, ?>> converters = new ArrayList<>(List.of(
        // 保留原有时间转换器
        JSR310DateConverters.ZonedDateTimeToDateConverter.INSTANCE,
        JSR310DateConverters.DateToZonedDateTimeConverter.INSTANCE,
        JSR310DateConverters.LongToDurationConverter.INSTANCE,
        JSR310DateConverters.DurationToLongConverter.INSTANCE,
        // 添加JobParameter双向转换器
        new JobParameterToDocumentConverter(),
        new DocumentToJobParameterConverter()
    ));

    return MongoCustomConversions.create(adapter -> {
        adapter.useNativeDriverJavaTimeCodecs();
        adapter.registerConverters(converters);
    });
}

为什么之前的转换器未生效

你之前的代码存在两个关键错误:

  1. 手动实例化MongoCustomConversions后,又调用create()生成新实例但未返回,导致实际生效的是未正确配置的手动实例。
  2. 未通过MongoCustomConversions.create()的回调逻辑注册转换器,导致转换器无法被整合到MongoDB驱动的编解码器体系中。

内容的提问来源于stack exchange,提问作者Андрей Дикий

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 10:10:31