Spring Data MongoDB自定义转换器未生效引发类型转换异常
问题:MongoDB自定义ReadingConverter未生效,触发ClassCastException
场景还原
实体CommOutput定义属性:
private List<Map<String, Object>> calls;
自定义@ReadingConverter转换器:
public class ArrayListToMapConverter implements Converter<ArrayList, List<Map<String, Object>>> { @Override public List<Map<String, Object>> convert(ArrayList source) { System.out.println("**************il est dans convert***************"); List<Map<String, Object>> resultList = new ArrayList<>(); for (Object item : source) { if (item instanceof Map) { @SuppressWarnings("unchecked") Map<String, Object> mapItem = (Map<String, Object>) item; resultList.add(mapItem); } } return resultList; } }
创建自定义MongoConfig配置类注入带转换器的mongoTemplateCustom,查询时使用该模板,但业务层遍历calls时抛出异常:
java.lang.ClassCastException: class java.util.ArrayList cannot be cast to class java.util.Map
怀疑转换器未被调用。
问题根源分析
- 转换器类型匹配错误:当前转换器是将
ArrayList转为List<Map<String,Object>>,但实际MongoDB返回的是List内的元素为ArrayList(而非Map),需要的是将单个ArrayList元素转为Map,而非整个集合转集合。 - 配置类冲突:存在两个继承
MongoAutoConfiguration的配置类(MongoDaoTools和MongoConfig),导致Spring容器中Bean加载优先级混乱,自定义转换器的MongoCustomConversions未正确关联到mongoTemplateCustom的MappingMongoConverter。 - 字段投影影响:查询中使用
include("calls.segmentId")这类嵌套字段投影,MongoDB返回的嵌套数组结构可能与自定义转换器的处理逻辑冲突。
解决步骤
1. 修正转换器逻辑
将转换器改为将单个ArrayList转为Map,Spring Data MongoDB会自动遍历集合为每个元素应用转换器:
@ReadingConverter public class ArrayListToMapConverter implements Converter<ArrayList, Map<String, Object>> { @Override public Map<String, Object> convert(ArrayList source) { System.out.println("**************转换器已触发***************"); Map<String, Object> resultMap = new HashMap<>(); // 根据MongoDB中实际存储的ArrayList结构调整转换逻辑,示例为[key,value]成对结构 for (int i = 0; i < source.size(); i += 2) { if (i+1 < source.size()) { resultMap.put(source.get(i).toString(), source.get(i+1)); } } return resultMap; } }
2. 清理配置类冲突
删除MongoDaoTools配置类,或合并两个配置类,调整MongoConfig确保Bean正确初始化:
@Configuration public class MongoConfig extends MongoAutoConfiguration { private final DatabaseProperties properties; public MongoConfig(DatabaseProperties properties) { this.properties = properties; } @Bean @Primary public MongoClient mongoClient() { ConnectionString connectionString = new ConnectionString(properties.getDatabaseUri()); return MongoClients.create(connectionString); } @Bean @Primary public MongoDatabaseFactory mongoDatabaseFactory(MongoClient client) { return new SimpleMongoClientDatabaseFactory(client, properties.getDatabase()); } @Bean @Qualifier("mongoTemplateCustom") public MongoTemplate mongoTemplateCustom(MongoDatabaseFactory databaseFactory, MongoMappingContext mongoMappingContext) { DbRefResolver dbRefResolver = new DefaultDbRefResolver(databaseFactory); MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext); converter.setCustomConversions(customConversions()); converter.setTypeMapper(new DefaultMongoTypeMapper(null)); converter.afterPropertiesSet(); // 必须调用完成初始化 return new MongoTemplate(databaseFactory, converter); } @Bean @Primary public MongoCustomConversions customConversions() { List<Converter<?, ?>> converters = new ArrayList<>(); converters.add(new ArrayListToMapConverter()); return new MongoCustomConversions(converters); } @Bean @Primary public MongoMappingContext mongoMappingContext() throws Exception { MongoMappingContext context = new MongoMappingContext(); context.setInitialEntitySet(new HashSet<>(Collections.singleton(CommOutput.class))); // 指定映射实体 context.afterPropertiesSet(); return context; } }
关键修改:
- 注入
MongoMappingContext作为Bean,确保上下文初始化完成 - 调用
converter.afterPropertiesSet()完成转换器注册 - 指定映射实体类,避免自动扫描问题
3. 调整查询投影(可选)
若投影导致结构异常,先查询整个calls字段验证转换器是否生效:
// 修改fields部分,直接包含整个calls字段 query.fields().include("creationDate").include("_id").include("commStatus") .include("commId").include("personId").include("sendWay") .include("commForceTreatment").include("errors").include("sysSourceIds") .include("calls") .include("orderStatus").include("commVectorStatus") .include("distributorId");
4. 验证转换器触发
运行程序,查看控制台是否打印转换器内的日志,确认转换器已生效后,再根据实际数据调整转换逻辑。
内容的提问来源于stack exchange,提问作者BARHOUMI
相关产品推荐
相关产品推荐

