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); }); }
为什么之前的转换器未生效
你之前的代码存在两个关键错误:
- 手动实例化
MongoCustomConversions后,又调用create()生成新实例但未返回,导致实际生效的是未正确配置的手动实例。 - 未通过
MongoCustomConversions.create()的回调逻辑注册转换器,导致转换器无法被整合到MongoDB驱动的编解码器体系中。
内容的提问来源于stack exchange,提问作者Андрей Дикий
相关产品推荐
相关产品推荐

