我们在项目中使用了 Spring Integration 和 Spring Kafka。 从 Spring-boot 3.1.7 更新到 3.2.1 后,我们在消息序列化期间遇到异常。
鉴于流程:
@Bean
IntegrationFlow sendToKafkaFlow() {
return IntegrationFlow.from("sendToKafkaFlow.input")
.transform(Transformers.toJson()) // 1
// some more handlers/header enricher
// .headerFilter(JsonHeaders.RESOLVABLE_TYPE) // 2
.handle(Kafka.outboundChannelAdapter(kafkaTemplate) //3
.topic("someTopic")
)
.get();
}
每次我们用这个流程发送消息时,我们都会看到异常:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException:直接自引用导致循环(通过引用链:org.springframework.core.ResolvableType["componentType"]-org.springframework.core.ResolvableType["componentType"] )
步骤 (1) 中的转换器将创建 json__resolvableType 标头。 在 outboundChannelAdapter (3) 中,DefaultKafkaHeaderMapper 用于创建 kafka 标头。一旦此映射器映射 resolvableType,它将产生错误并且不会映射此标头。
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)
据我在调试器中看到的,componentType为空,将被 EmptyInstance (1) 替换。这个 EmptyInstance 也会被分析。 EmptyInstance 本身将再次返回 componentType (2) 的 EmptyInstance。这就导致了自我参照。 (3)
如果我们删除此标头(流程 2),则标头将丢失并且异常消失。
我尝试在 application.yml 中切换失败自引用
spring:
jackson:
serialization:
fail-on-self-references: false
但这不起作用。 不幸的是,我们经常有这种模式(在不同的应用程序中)。 (转换、做某事、发送到 kafka)。因此,添加 HeaderMapper 的修改或更改流程,在 kafka 模板中进行 json 序列化需要付出一些努力。
还有什么,我们可以尽量避免这种情况直接自引用导致循环可解析类型头异常?
jackson-databind
应该可以解决您的问题。
如果您使用 Maven,请在 pom.xml 文件中尝试此操作:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version>
</dependency>
请参阅这家工厂:
Transformers.toJson(JsonObjectMapper<?, ?> jsonObjectMapper)
这样你就可以注入由 Spring Boot 自动配置的
ObjectMapper
。
您还可以使用
headerEnricher()
来使用简单的 JsonHeaders.RESOLVABLE_TYPE
内容覆盖 Class
标头:headers.get(JsonHeaders.RESOLVABLE_TYPE, ResolvableType.class).getRawClass()
。