Spring Boot升级至3.4.5后Jackson反序列化ResponseTO接口报错求助
Spring Boot 3.4.5升级后Jackson反序列化第三方接口报错解决
问题现象
将Spring Boot从3.2.4升级至3.4.5后,接口接收请求时抛出Jackson反序列化错误:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `****.ResponseTO` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 341] (through reference chain: *****.OperationResult["response"])
相关代码:
@RestController @RequestMapping("/api/operations") public class OperationController { @PostMapping public ResponseEntity<String> testCall(@RequestBody OperationResult operationResult) { log.info("Operation Name :: {}", operationResult.getOperationName()); return ResponseEntity.ok("success"); } }
import lombok.*; @Builder @ToString @EqualsAndHashCode @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class OperationResult { private Long id; private Long operationName; // 第三方提供的接口,存在多个具体实现类 private ResponseTO response; }
原因分析
ResponseTO是第三方库提供的接口/抽象类,Jackson反序列化OperationResult的response字段时,无法自动推断应该使用哪个具体实现类实例化。Spring Boot 3.4.x对应的Jackson版本升级后,默认反序列化策略对抽象类型的处理更严格,导致之前可能隐式生效的兼容逻辑不再起作用。
解决方案
1. 指定默认实现类(适用于单一实现场景)
如果ResponseTO只有一个常用的具体实现类,直接在字段上添加@JsonDeserialize注解指定实现类:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import lombok.*; @Builder @ToString @EqualsAndHashCode @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class OperationResult { private Long id; private Long operationName; // 指定具体实现类 @JsonDeserialize(as = ConcreteResponseTO.class) private ResponseTO response; }
2. 自定义反序列化器(适用于多实现动态判断场景)
如果存在多个ResponseTO实现类,且需要根据请求JSON的字段动态判断,编写自定义反序列化器:
步骤1:实现自定义反序列化器
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; public class ResponseTODeserializer extends JsonDeserializer<ResponseTO> { @Override public ResponseTO deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node = p.getCodec().readTree(p); // 根据JSON中的标识字段判断使用哪个实现类 String type = node.get("type").asText(); return switch (type) { case "TYPE_A" -> ctxt.readValue(node.traverse(p.getCodec()), ResponseTOImplA.class); case "TYPE_B" -> ctxt.readValue(node.traverse(p.getCodec()), ResponseTOImplB.class); default -> throw new IllegalArgumentException("Unsupported ResponseTO type: " + type); }; } }
步骤2:注册反序列化器
在OperationResult的字段上指定自定义反序列化器:
@JsonDeserialize(using = ResponseTODeserializer.class) private ResponseTO response;
3. 使用Jackson混合注解(适用于多实现+类型标识场景)
如果请求JSON中包含类型标识字段,且无法修改第三方ResponseTO接口源码,使用Jackson的MixIn功能添加类型识别注解:
步骤1:定义MixIn接口
import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; abstract class ResponseTOMixIn { @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type" // JSON中标识类型的字段名 ) @JsonSubTypes({ @JsonSubTypes.Type(value = ResponseTOImplA.class, name = "TYPE_A"), @JsonSubTypes.Type(value = ResponseTOImplB.class, name = "TYPE_B") }) abstract void dummyMethod(); // 无需实现,仅用于承载注解 }
步骤2:配置Jackson注册MixIn
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.fasterxml.jackson.databind.ObjectMapper; @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); // 为第三方接口绑定MixIn注解 mapper.addMixIn(ResponseTO.class, ResponseTOMixIn.class); return mapper; } }
4. 全局配置类型映射(适用于全局统一处理)
如果需要全局为ResponseTO指定默认实现,在Spring Boot配置类中设置Jackson的类型映射:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.fasterxml.jackson.databind.ObjectMapper; @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); // 全局指定ResponseTO的默认实现类 mapper.registerSubtypes(ConcreteResponseTO.class); return mapper; } }
内容的提问来源于stack exchange,提问作者nitinty
相关产品推荐
相关产品推荐

