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

SpringBoot读取SNS转SQS消息时反序列化失败问题求助

解决SpringBoot中SNS转SQS消息的反序列化问题

我一眼就看出问题所在了:SNS转发到SQS的消息是JSON嵌套字符串的结构——外层是SNS的通知JSON,其中Message字段的值本身又是一个转义后的JSON字符串(也就是你要解析的PushEvent内容)。但你的SqsMessage类直接把message字段定义成PushEvent<?>类型,Jackson会默认把这个字符串当成普通文本去转成PushEvent对象,可PushEvent并没有接收字符串参数的构造方法,这就直接导致了反序列化失败。

下面给你几种可行的解决方案,按需选择:

方案1:先接收字符串,手动解析(最直接易维护)

先把Message字段当成字符串接收,再在业务代码里手动反序列化成PushEvent,逻辑清晰,不容易出问题:

第一步:修改SqsMessage类

把message字段的类型从PushEvent<?>改成String:

public class SqsMessage {
    @JsonProperty("MessageId")
    String messageId;
    @JsonProperty("Message")
    String message; // 改为String类型接收嵌套的JSON字符串

    public SqsMessage() {}

    public SqsMessage(String messageId, String message) {
        this.messageId = messageId;
        this.message = message;
    }

    // getter和setter同步更新类型
    public String getMessageId() { return messageId; }
    public void setMessageId(String messageId) { this.messageId = messageId; }
    public String getMessage() { return message; }
    public void setMessage(String message) { this.message = message; }
}

第二步:修改监听器代码,手动解析PushEvent

注入SpringBoot默认提供的ObjectMapper,在监听器里完成字符串到PushEvent的转换:

@Autowired
private ObjectMapper objectMapper;

@SqsListener(QUEUE_NAME)
public void receiveMessage(SqsMessage message, @Header("SenderId") String senderId) {
    logger.info("Service A received messageId: {}", message.getMessageId());
    
    // 手动解析嵌套的JSON字符串
    try {
        PushEvent<?> pushEvent = objectMapper.readValue(message.getMessage(), PushEvent.class);
        logger.info("Service A received author: {}", pushEvent.getAuthor());
        logger.info("Service A received error: {}", pushEvent.getError());
    } catch (JsonProcessingException e) {
        logger.error("Failed to parse PushEvent from message content", e);
        // 这里可以添加异常处理逻辑,比如消息重试、死信队列投递等
    }
    
    logger.info("Service A senderId: {}", senderId);
}

方案2:用Jackson注解自动处理嵌套JSON

如果你不想手动写解析逻辑,可以通过Jackson的注解让框架自动处理这个嵌套字符串:

方式2.1:给PushEvent添加字符串参数的构造方法

给PushEvent加一个接收字符串的构造方法,并用@JsonCreator标记,让Jackson知道用这个方法来处理字符串输入:

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class PushEvent<T> {
    String author;
    String error;

    // 新增字符串参数的构造方法,用于Jackson反序列化
    @JsonCreator
    public PushEvent(String jsonString) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        PushEvent<?> event = mapper.readValue(jsonString, PushEvent.class);
        this.author = event.getAuthor();
        this.error = event.getError();
    }
}

注意:如果PushEvent的泛型T需要实际使用,你需要调整这个构造方法的泛型处理逻辑。

方式2.2:自定义反序列化器

如果构造方法的方式不够灵活,可以写一个专属的反序列化器:

  1. 编写反序列化器类:
public class PushEventDeserializer extends JsonDeserializer<PushEvent<?>> {
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public PushEvent<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        // 读取嵌套的JSON字符串,再转成PushEvent对象
        String jsonString = p.getText();
        return objectMapper.readValue(jsonString, PushEvent.class);
    }
}
  1. 在SqsMessage的message字段上添加注解,指定使用自定义反序列化器:
public class SqsMessage {
    @JsonProperty("MessageId")
    String messageId;
    @JsonProperty("Message")
    @JsonDeserialize(using = PushEventDeserializer.class) // 指定反序列化器
    PushEvent<?> message;

    // 构造方法、getter、setter保持不变
}

方案3:优化Spring Cloud AWS的消息转换器配置

你的当前配置里重复添加了MappingJackson2MessageConverter,可以优化一下,避免冗余配置(这个是辅助优化,需要配合上面的方案1或2一起使用):

@Configuration
public class SpringCloudAwsConfig {
    @Bean
    public QueueMessageHandlerFactory queueMessageHandlerFactory() {
        QueueMessageHandlerFactory factory = new QueueMessageHandlerFactory();
        MappingJackson2MessageConverter messageConverter = new MappingJackson2MessageConverter();
        messageConverter.setStrictContentTypeMatch(false);
        // 只需要设置一次参数解析器即可,无需重复添加消息转换器
        factory.setArgumentResolvers(Collections.singletonList(new PayloadArgumentResolver(messageConverter)));
        return factory;
    }

    // 其他Bean(queueMessagingTemplate、notificationMessagingTemplate)保持不变
}

再帮你梳理下错误根源

从错误日志里的消息结构能清楚看到问题:

{
  "Type" : "Notification",
  "MessageId" : "56639e45-586e-5281-9204-cbeed1890b81",
  "TopicArn" : "arn:aws:sns:eu-west-1:xxxxxxxxxxxx:eventNow",
  "Message" : "{\"author\":\"Peter Smith\",\"error\":\"error of test\"}",
  ...
}

这里的Message值是一个转义后的JSON字符串,而不是直接的JSON对象。Jackson默认会把它当成普通字符串处理,但你的SqsMessage里把message定义成PushEvent类型,Jackson就会尝试把字符串转成PushEvent,但找不到对应的构造方法,于是抛出了MismatchedInputException。

内容的提问来源于stack exchange,提问作者Stéphane GRILLON

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:48:22