Spring Boot 3.1.3下JmsListener泛型容器对象反序列化问题
系统背景
我正在构建一个事件驱动系统,由多个通过领域事件通信的服务组成,所有服务均基于Spring Boot 3.1.3,使用ActiveMQ Artemis作为消息中间件。
核心代码定义
我定义了统一各服务行为的接口与通用代码:
interface DomainEvent
data class DomainEventContainer<D : DomainEvent>( val event: D, val type: DomainEventType, val createdBy: DomainEventOrigin, val createdAt: Instant )
fun interface BaseDomainEventProducer<T : DomainEvent> { fun produce(event: T): DomainEventContainer<T>? }
fun interface BaseDomainEventConsumer<T : DomainEvent> { fun consume(container: DomainEventContainer<T>) }
生产者实现
生产者端的核心发送逻辑如下:
fun produce(event: DomainEvent) { // some logic here val container = DomainEventContainer(event, type, DomainEventOrigin.MY_SERVICE, Instant.now()) return try { log.info("Sending event $container") jmsTemplate.convertAndSend(destination, container) container } catch (e: Exception) { log.info("Error during sending event $container ", e) null } }
消费者反序列化失败问题
我尝试在消费者端直接接收泛型容器对象:
@JmsListener(destination = "\${random.artemis.queue}") override fun consume(message: DomainEventContainer<MyCustomDomainEvent>) { eventHandler.handle(container.event) }
但触发Jackson反序列化异常,报错信息如下:
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of
DomainEvent(no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
手动解决方案(繁琐)
目前只能通过手动在每个监听器中处理反序列化逻辑,比如:
方式一:直接处理jakarta.jms.Message
@JmsListener(destination = "\${random.artemis.queue}") override fun consume(message: Message) { val container = objectMapper.readValue( message.getBody(String::class.java), object : TypeReference<DomainEventContainer<EmploymentRegisteredDomainEvent>>(){}) eventHandler.handle(container.event) }
方式二:使用org.springframework.messaging.Message
@JmsListener(destination = "\${random.artemis.queue}") override fun consume(message: Message<DomainEventContainer<MyCustomDomain>>) { val container = message.payload eventHandler.handle(container.event) }
但这种重复逻辑应该由平台统一处理,而非每个监听器都手动编写。
疑问与需求
Spring Boot的RabbitMQ模块可以直接实现自动处理:发送DomainEventContainer<MyCustomDomain>后,消费者可直接通过如下代码接收:
@RabbitListener(queues = ["random.rabbit.queue"]) fun consume(event: DomainEvent<MyCustomDomain>)
为何Spring JMS模块无法实现类似的自动反序列化?是否有对应的配置项可以实现,避免手动重复逻辑?
内容的提问来源于stack exchange,提问作者mr.nothing

