Spring Boot2.6.1调用MongoRepository.findById实例化抽象父类报错
问题描述
项目运行时抛出异常:Failed to instantiate abstract superclass 'ClassName' using public constructor with arguments - Mongodb
触发场景:
- 业务实体定义如下:
@Document(collection = "document1") @Getter @ToString public abstract class DocumentA { private String name; private String lName; public DocumentA(){} } @lombok.Getter public class DocumentB extends DocumentA { private String someFields; public DocumentB() {} }
- 对应Mongo Repository接口方法定义如下,调用该
findById()方法时触发异常,方法预期返回子类DocumentB的实例:
DocumentA findById(String id);
- 相同代码在Spring Boot 2.1.5版本可正常运行,升级到2.6.1版本后开始抛出该异常。
问题根因
Spring Data MongoDB在版本迭代中调整了抽象实体的反序列化实例化逻辑:
- Spring Boot 2.1.x对应的Spring Data MongoDB版本,反序列化时会自动扫描抽象类的所有实现子类,结合MongoDB文档中存储的
_class类型元数据匹配对应子类实例化,即使方法返回值声明为抽象父类也能正常返回子类实例。 - Spring Boot 2.6.1对应的Spring Data MongoDB版本默认开启严格类型映射校验:当方法返回值声明为抽象类时,框架会优先尝试直接实例化该声明类型,不会自动扫描子类做匹配。抽象类本身无法被实例化,就会抛出上述实例化失败的错误。
如果历史写入MongoDB的文档未存储_class类型字段,框架无法获知文档对应的实际实现类,会直接触发该错误。
修复方案
根据业务场景选择对应方案即可:
- 方案1(单实现类场景推荐,改造成本最低):直接修改Repository方法的返回值声明为具体实现类,避免框架尝试实例化抽象类:
DocumentB findById(String id);
- 方案2(多子类多态场景推荐):显式配置继承映射规则,在抽象父类上指定类型元信息,强制框架根据文档存储的
_class字段匹配对应实现类,单实现类场景可直接指定默认实现类:
@Document(collection = "document1") @Getter @ToString // 指定默认实现类,多子类场景可配合@TypeAlias注解区分不同子类类型 @org.springframework.data.mongodb.core.mapping.DocumentType(DocumentB.class) public abstract class DocumentA { private String name; private String lName; public DocumentA(){} }
- 方案3(兼容旧逻辑):自定义Mongo映射转换器,关闭严格类型校验,开启自动子类扫描,该方案会增加反序列化时的性能开销,非必要不推荐。
内容的提问来源于stack exchange,提问作者Neeraj Kunturkar
相关产品推荐
相关产品推荐

