You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 19:29:50