Spring Boot升级后ResolvableType序列化循环引用异常求助
背景
项目基于Spring Integration和Spring Kafka,将Spring Boot从3.1.7升级到3.2.1后,消息发送时出现Jackson序列化循环引用异常。
核心代码
@Bean IntegrationFlow sendToKafkaFlow() { return IntegrationFlow.from("sendToKafkaFlow.input") .transform(Transformers.toJson()) // 1. 生成json__resolvableType头 // 其他处理器/头增强器 // .headerFilter(JsonHeaders.RESOLVABLE_TYPE) // 2. 注释掉的头过滤代码 .handle(Kafka.outboundChannelAdapter(kafkaTemplate) //3. Kafka出站适配器 .topic("someTopic") ) .get(); }
异常详情
每次走这个流程发消息都会抛错:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Direct self-reference leading to cycle (through reference chain: org.springframework.core.ResolvableType["componentType"]-org.springframework.core.ResolvableType["componentType"])
完整堆栈:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Direct self-reference leading to cycle (through reference chain: org.springframework.core.ResolvableType["componentType"]->org.springframework.core.ResolvableType["componentType"]) at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1308) (3) at com.fasterxml.jackson.databind.ser.BeanPropertyWriter._handleSelfReference(BeanPropertyWriter.java:948) (2) at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:726) at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:772) at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) (1) at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:732) at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:772) at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:479) at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:318) at com.fasterxml.jackson.databind.ObjectMapper._writeValueAndClose(ObjectMapper.java:4719) at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsBytes(ObjectMapper.java:3987)
问题根因
步骤1的Transformers.toJson()会自动添加json__resolvableType头,步骤3的Kafka出站适配器用DefaultKafkaHeaderMapper映射这个头时,触发循环引用:当ResolvableType的componentType为null时,会被替换成EmptyInstance,而这个EmptyInstance的componentType又指向自身,Jackson序列化时就会报错。
已试过的无效操作
- 手动加步骤2的头过滤可以解决,但项目里有大量类似流程,改起来成本太高;
- 在application.yml里配置Jackson关闭自引用失败开关,完全没效果:
spring: jackson: serialization: fail-on-self-references: false
可行解决方案
1. 全局配置Jackson处理ResolvableType序列化
给Jackson加个自定义序列化器,专门处理ResolvableType,避免循环引用:
@Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new SimpleModule() .addSerializer(ResolvableType.class, new JsonSerializer<ResolvableType>() { @Override public void serialize(ResolvableType value, JsonGenerator gen, SerializerProvider serializers) throws IOException { if (value == null || ResolvableType.NONE.equals(value)) { gen.writeNull(); return; } // 只序列化类型名称,跳过会引发循环的字段 gen.writeString(value.getType().getTypeName()); } })); return mapper; } }
2. 全局配置KafkaHeaderMapper过滤该头
不用修改每个IntegrationFlow,直接全局配置DefaultKafkaHeaderMapper,自动忽略json__resolvableType:
@Bean public KafkaHeaderMapper kafkaHeaderMapper() { DefaultKafkaHeaderMapper mapper = new DefaultKafkaHeaderMapper(); // 感叹号表示排除该头 mapper.setOutboundHeaderPatterns(Collections.singletonList("!" + JsonHeaders.RESOLVABLE_TYPE)); return mapper; }
3. 关闭Transformers.toJson()生成ResolvableType头
在使用Transformers.toJson()时,直接配置不生成这个头:
.transform(Transformers.toJson() .resolvableType(false))
如果多处用到,可以封装成一个统一的Transformer bean,避免重复配置。
4. 升级依赖版本
检查Spring Integration或Spring Kafka的后续版本,看是否官方已经修复了ResolvableType的循环引用问题,升级到最新稳定版可能直接解决。
内容的提问来源于stack exchange,提问作者Jonathan H

