【发布时间】:2018-09-09 00:20:26
【问题描述】:
我正在使用带有 JSON 消息序列化的 rabbitmq 的 Spring-boot。使用 Direct-Reply-to 功能的回复无法反序列化 java.util.List 容器中的类。
在Jackson2JsonMessageConverter.fromMessage() 中使用我的调试器,MessageProperties 表明__TypeID__ 正确设置为java.util.ArrayList。但是 __ContentTypeId__ 是 java.lang.Object 是不正确的,因为我期待 FooDto (我假设......)。
异常消息是:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to FooDto
请注意,我使用的是 spring-rabbit 1.7.3 而不是 v2.0,因此不能将 ParameterizedTypeReference 用于 rabbitTemplate.convertSendAndReceiveAsType() 方法。
我尝试使用DefaultClassMapper 和DefaultJackson2JavaTypeMapper(TypePrecedence 在TYPE_ID 和INFERRED 下测试)但没有成功:
private DefaultJackson2JavaTypeMapper classMapper() {
final DefaultJackson2JavaTypeMapper classMapper = new DefaultJackson2JavaTypeMapper();
final Map<String, Class<?>> idClassMapping = new HashMap<>();
idClassMapping.put(FooDto.class.getSimpleName(), FooDto.class);
classMapper.setIdClassMapping(idClassMapping);
return classMapper;
}
现在的例外是:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to FooDto
到目前为止,我的解决方法是使用原始类型的数组,即FooDto[]。
库版本: - 弹簧启动 1.5.6 - 兔MQ:3.7.4 - 春兔 1.7.3
Maven pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>
</dependencies>
有效的 Pom:
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-amqp</artifactId>
<version>1.7.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.7.3.RELEASE</version>
</dependency>
RabbitMQ 配置:
@Configuration
@EnableRabbit
public class MessagingConfiguration implements ShutdownListener {
// FIXME: List<FooDto> in the direct-reply response contains ArrayList<Object> due to __ContentTypeID__ == SimpleObject/Object . __TypeID__ is correctly ArrayList
@Bean
public List<Declarable> bindings() {
final List<Declarable> declarations = new ArrayList<>();
final FanoutExchange exchange = new FanoutExchange("fx", true, false);
final Queue queue = QueueBuilder.durable("orders").build();
declarations.add(exchange);
declarations.add(queue);
declarations.add(BindingBuilder.bind(queue).to(exchange));
return declarations;
}
// @Bean
// public DefaultClassMapper classMapper() {
// DefaultClassMapper classMapper = new DefaultClassMapper();
// Map<String, Class<?>> idClassMapping = new HashMap<>();
// idClassMapping.put("FooDto", FooDto.class);
// java.util.List<FooDto>
// classMapper.setIdClassMapping(idClassMapping);
// return classMapper;
// }
//
// @Bean
// public DefaultClassMapper classMapper() {
// final DefaultClassMapper typeMapper = new DefaultClassMapper();
// // typeMapper.setDefaultType(new ArrayList<FooDto>().getClass());
// typeMapper.setDefaultType(FooDto[].class);
// return typeMapper;
// }
@Bean
public Jackson2JsonMessageConverter jsonConverter() {
// https://stackoverflow.com/questions/40491628/jackson-configuration-to-consume-list-of-records-in-rabbitmq
// https://github.com/FasterXML/jackson-core/issues/295
final Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter();
converter.setTypePrecedence(TypePrecedence.TYPE_ID);
// converter.setClassMapper(classMapper());
return converter;
}
@ConditionalOnProperty(name = "consumer", havingValue = "true")
@Bean
public ConsumerListener listenerConsumer() {
return new ConsumerListener();
}
@ConditionalOnProperty(name = "producer", havingValue = "true")
@Bean
public ProducerListener listenerProducer() {
return new ProducerListener();
}
@Bean
public RabbitAdmin rabbitAdmin(final CachingConnectionFactory connectionFactory) {
return new RabbitAdmin(connectionFactory);
}
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(
final ConnectionFactory connectionFactory) {
// Setting the annotation @RabbitListener to use Jackson2JsonMessageConverter
final SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(jsonConverter());
factory.setConcurrentConsumers(5);
factory.setMaxConcurrentConsumers(5);
return factory;
}
@Bean
public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jsonConverter()); // convert all sent messages to JSON
rabbitTemplate.setReplyTimeout(TimeUnit.SECONDS.toMillis(3));
rabbitTemplate.setReceiveTimeout(TimeUnit.SECONDS.toMillis(3));
return rabbitTemplate;
}
@Override
public void shutdownCompleted(final ShutdownSignalException arg0) {
}
}
侦听器在交换“fx”上使用包含来自队列“订单”的 MyQuery 对象的消息:
public class ConsumerListener {
private static final Logger log = LoggerFactory.getLogger(ConsumerListener.class);
@RabbitListener(queues = { "orders" })
public FooDto[] receiveMessage(final MyQuery query) {
log.info(query);
List<FooDto> response = new ArrayList<>();
response.add(new FooDto());
response.add(new FooDto());
response.add(new FooDto());
return response;
}
}
向 Exchange 发送消息时使用的 POJO: 类 MyQuery { 私有字符串内容=“测试”;
public MyQuery();
public String toString() {
return content;
}
}
用于响应的 POJO: 类FooDto { 私有字符串内容 = "foo";
public FooDto();
public String toString() {
return content;
}
}
【问题讨论】:
标签: json spring-boot rabbitmq spring-rabbit