Spring Cloud @SqsListener MessageConversionException:无法从 [java.lang.String] 转换为 GenericMessage
Posted
技术标签:
【中文标题】Spring Cloud @SqsListener MessageConversionException:无法从 [java.lang.String] 转换为 GenericMessage【英文标题】:Spring Cloud @SqsListener MessageConversionException: Cannot convert from [java.lang.String] for GenericMessage 【发布时间】:2018-11-24 19:59:56 【问题描述】:我在尝试使用 SQS 消息时看到以下异常:
org.springframework.messaging.converter.MessageConversionException:
Cannot convert from [java.lang.String] to [com.example.demo.Foo] for GenericMessage [payload=, headers=LogicalResourceId=my-queue, ApproximateReceiveCount=1, SentTimestamp=1529021258825, ReceiptHandle=xxxx, Visibility=org.springframework.cloud.aws.messaging.listener.QueueMessageVisibility@47ce6922, SenderId=xxxx, lookupDestination=my-queue, ApproximateFirstReceiveTimestamp=1529021264456, MessageId=xxxx]
at org.springframework.messaging.handler.annotation.support.PayloadArgumentResolver.resolveArgument(PayloadArgumentResolver.java:144)
at org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:116)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:137)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:109)
at org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler.handleMatch(AbstractMethodMessageHandler.java:515)
at org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler.handleMessageInternal(AbstractMethodMessageHandler.java:473)
at org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler.handleMessage(AbstractMethodMessageHandler.java:409)
at org.springframework.cloud.aws.messaging.listener.SimpleMessageListenerContainer.executeMessage(SimpleMessageListenerContainer.java:205)
at org.springframework.cloud.aws.messaging.listener.SimpleMessageListenerContainer$MessageExecutor.run(SimpleMessageListenerContainer.java:342)
at org.springframework.cloud.aws.messaging.listener.SimpleMessageListenerContainer$SignalExecutingRunnable.run(SimpleMessageListenerContainer.java:397)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Spring Boot 代码如下:
@Configuration
@EnableSqs
public class AmazonSqsConfiguration
@Bean
public AmazonSQS amazonSQSAsync()
return AmazonSQSAsyncClientBuilder.standard()
.withRegion(Regions.US_WEST_2)
.build();
@Service
public class MyService
// Throws MessageConversionException
@SqsListener("my-queue")
public void listen(Foo payload)
// Works fine
@SqsListener("my-queue")
public void listen(String payload)
我正在使用org.springframework.cloud:spring-cloud-aws-messaging:2.0.0.RC2
我的类路径中确实有 Jackson 2 库,因此 PayloadArgumentResolver
正在尝试使用 MappingJackson2MessageConverter
反序列化我的消息负载。但是,由于 SQS 消息缺少 contentType
标头并且 strictContentTypeMatch
设置为 true,所以 canConvertFrom
返回 false。
https://github.com/spring-projects/spring-framework/blob/f5e8f4983f7653169f3da8a3287499fce93cadd4/spring-messaging/src/main/java/org/springframework/messaging/converter/AbstractMessageConverter.java#L237
我看不到如何为 SQS 消息设置 contentType
标头 - 我错过了什么吗?
Spring Cloud QueueMessageHandler
是否应该将 strictContentTypeMatch
设置为 true?
https://github.com/spring-cloud/spring-cloud-aws/blob/6a7c3c31709d4239131b27936de29385df414d41/spring-cloud-aws-messaging/src/main/java/org/springframework/cloud/aws/messaging/listener/QueueMessageHandler.java#L217
【问题讨论】:
你解决了这个问题吗? 【参考方案1】:遇到同样的问题,我会根据谁生成消息以两种方式之一回答问题
是的,可以在消息上设置contentType
,如果您控制正在生成的消息,这是首选。在 AWS 控制台中,当您手动发送消息时,有一个“消息属性”选项卡。您将添加名称为contentType
和值application/json
的属性。 AWS SDK 调用应该允许您从应用程序代码中执行相同的操作。
对于 AWS 生成的没有指定内容类型的消息,例如 S3 事件,您实际上需要将 strictContentMatch
设置为 false。这记录在这里:
http://cloud.spring.io/spring-cloud-static/spring-cloud-aws/2.0.0.RELEASE/multi/multi__messaging.html#_consuming_aws_event_messages_with_amazon_sqs
该文档令人困惑,因为它说“没有 mime 类型的标题”,但标题的实际名称是 contentType
,正如您自己发现的那样。
【讨论】:
【参考方案2】:在“发送”和“收听”方法中都有一个一致的转换器:
/** Provides a deserialization template for incoming SQS messages */
@Bean
public QueueMessageHandlerFactory queueMessageHandlerFactory(MessageConverter messageConverter)
var factory = new QueueMessageHandlerFactory();
factory.setArgumentResolvers(singletonList(new PayloadArgumentResolver(messageConverter)));
return factory;
/** Provides a serialization template for outgoing SQS messages */
@Bean
public QueueMessagingTemplate queueMessagingTemplate(AmazonSQSAsync amazonSQSAsync, MessageConverter messageConverter)
return new QueueMessagingTemplate(amazonSQSAsync, (ResourceIdResolver) null, messageConverter);
/** Provides JSON converter for SQS messages */
@Bean
protected MessageConverter messageConverter(ObjectMapper objectMapper)
var converter = new MappingJackson2MessageConverter();
converter.setObjectMapper(objectMapper);
// Serialization support:
converter.setSerializedPayloadClass(String.class);
// Deserialization support: (suppress "contentType=application/json" header requirement)
converter.setStrictContentTypeMatch(false);
return converter;
在原始接受的答案中查看详细信息。感谢@wrschneider。
注意:上面的例子注入并设置了 ObjectMapper。对于 HTTP REST 控制器的一致性(如果有),这是可选的。
进口:
import static java.util.Collections.singletonList;
import com.amazonaws.services.sqs.AmazonSQSAsync;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cloud.aws.core.env.ResourceIdResolver;
import org.springframework.cloud.aws.messaging.config.QueueMessageHandlerFactory;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.handler.annotation.support.PayloadArgumentResolver;
用法:
@SqsListener("my-queue")
public void listen(Foo payload)
public void send(Foo dto)
queueMessagingTemplate.convertAndSend(url, dto);
【讨论】:
以上是关于Spring Cloud @SqsListener MessageConversionException:无法从 [java.lang.String] 转换为 GenericMessage的主要内容,如果未能解决你的问题,请参考以下文章
防止 spring-cloud-aws-messaging 尝试停止队列
设置手动确认 SQS 消息的 Spring Cloud AWS 问题