Java 21升级后Spring Boot 3的SQS监听器无法解析自定义对象消息
Java 21升级后Spring Boot 3的SQS监听器无法解析自定义对象消息
我正在将一个基于微服务的系统从Java 17升级到Java 21,运行在Spring Boot 3和Spring Framework 6上。我的应用程序将自定义对象(Event)发送到AWS SQS队列,但我的消息监听器期望接收String类型。这在Java 17中运行正常,但升级后,监听器因为JSON解析问题无法处理消息。
问题
当使用 sqsTemplate.send(queueName, myEvent) 发送Event对象时,监听器抛出以下异常:
Caused by: com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'no': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false') at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 3] at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2572) at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2598) at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2606) at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:765) at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._reportInvalidToken(ReaderBasedJsonParser.java:3018) at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._reportInvalidToken(ReaderBasedJsonParser.java:2996) at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._matchToken(ReaderBasedJsonParser.java:2761) at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._matchNull(ReaderBasedJsonParser.java:2747) at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:750) at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4992) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4898) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3848) at org.springframework.messaging.converter.MappingJackson2MessageConverter.convertFromInternal(MappingJackson2MessageConverter.java:251) ... 27 common frames omitted
调试后发现,MappingJackson2MessageConverter 内部的 payload.toString() 返回的是对象引用而非JSON字符串:
no.com.common.Event@6f8e5dfd
配置详情
SQS监听器与消息转换
监听器代码:
@SqsListener(value = "${sqs.queue.event.in}", acknowledgementMode = SqsListenerAcknowledgementMode.MANUAL) public void receiveHourEvent(Acknowledgement acknowledgement, String event){ LOG.info("SQSListener triggered"); }
容器工厂与模板配置:
@Bean SqsMessageListenerContainerFactory<Object> defaultSqsListenerContainerFactory(SqsAsyncClient sqsAsyncClient, SqsMessagingMessageConverter sqsMessagingMessageConverter, SqsReceiverErrorHandler sqsReceiverErrorHandler, @Value("${sqs.poll.timeout}") Duration pollTimeout, @Value("${sqs.poll.messagevisibility}") Duration pollMessageVisibility) { return SqsMessageListenerContainerFactory .builder() .configure(options -> options .maxMessagesPerPoll(10) .pollTimeout(pollTimeout) .messageVisibility(pollMessageVisibility) .messageConverter(sqsMessagingMessageConverter)) .sqsAsyncClient(sqsAsyncClient) .messageInterceptor(new BreadcrumbMessageInterceptor()) .errorHandler(sqsReceiverErrorHandler) .build(); } @Bean SqsTemplate sqsTemplate(SqsAsyncClient sqsAsyncClient, SqsMessagingMessageConverter sqsMessagingMessageConverter) { SqsTemplate sqsTemplate = SqsTemplate.builder() .sqsAsyncClient(sqsAsyncClient) .messageConverter(sqsMessagingMessageConverter) .build(); return sqsTemplate; } @Bean public SqsMessagingMessageConverter sqsMessagingMessageConverter(SqsExtendedClient sqsExtendedClient) { ExtendedSqsMessagingMessageConverter converter = new ExtendedSqsMessagingMessageConverter(sqsExtendedClient); converter.setObjectMapper(JsonMappers.getObjectMapper()); return converter; }
ObjectMapper配置:
private static ObjectMapper initializeMapper() { final ObjectMapper mapper = new ObjectMapper(); mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) .setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE) .setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.registerModule(new JavaTimeModule()); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); mapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true); mapper.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS); mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true); return mapper; } public static <T> T fromJson(String text, Class<T> type) { try { return getObjectMapper().readValue(text, type); } catch (IOException e) { throw new RuntimeException(e); } } public static <T> String toJson(T value) { try { return getObjectMapper().writeValueAsString(value); } catch (IOException e) { throw new RuntimeException(e); } }
Event对象
public class Event { String eventName; Instant dateTime; }
发送消息的代码:
sqsTemplate.send(queueName, myEvent);
我已经验证手动调用 toJson(myEvent) 可以正确序列化对象,也确认了 sqsMessagingMessageConverter 已正确设置到 SqsTemplate 中。调试时看到 MappingJackson2MessageConverter 里的这行代码返回的是对象引用:
return this.objectMapper.readValue(payload.toString(), javaType)
依赖版本
<!--JSON serialization--> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.17.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.17.2</version> </dependency>
疑问
为什么 payload 被当作对象引用而不是JSON字符串处理?是我的ObjectMapper配置有遗漏,还是需要调整SqsMessagingMessageConverter的序列化处理方式?
备注:内容来源于stack exchange,提问作者Zoe
相关产品推荐
相关产品推荐

