为什么 messageConverter.fromMessage() 不转换数组列表? java.lang.ClassCastException: java.lang.String 无法施放
Why messageConverter.fromMessage() is not converting an arraylist? java.lang.ClassCastException: java.lang.String cannot be cast
我想发送包含销售列表的 JMS 消息并通过 onMessage()
接收。据我所知,springframework.jms.support.converter.MessageConverter
应该起到转换或转换消息的作用,因此我不必创建 XSD 或额外的映射 classes。我的意思是,我期待下面的测试发送一条消息和 (SalesMessage) messageConverter.fromMessage(message)
足以转换和实例化 SalesMessage
的对象。
基本上,错误是java.lang.ClassCastException: java.lang.String cannot be cast to ...SalesMessage
。
我的测试
AnnotationConfigApplicationContext context;
MyMessageSender ms;
JmsListenerEndpointRegistry bean;
@Before
public void setUpBeforeClass() throws Exception {
context = new AnnotationConfigApplicationContext(AppConfig.class);
ms = context.getBean(MyMessageSender.class);
}
@Test
public void test2() {
Sale s = new Sale("product_type", 1L);
List<Sale> l = new ArrayList<Sale>();
l.add(s);
SalesMessage sm = new SalesMessage(l, 1);
ms.sendMessage(sm.toString());
bean = context.getBean(JmsListenerEndpointRegistry.class);
for (MessageListenerContainer listenerContainer : bean.getListenerContainers()) {
DefaultMessageListenerContainer container = (DefaultMessageListenerContainer) listenerContainer;
container.shutdown();
}
}
MyJmsListener
@Component
public class MyJmsListener {
@Autowired
private MessageConverter messageConverter;
@JmsListener(destination = AppConfig.QUEUE_NAME)
public void handleMessage(Message message) {
try {
System.out.println("received: "+message);
//this prints received: ActiveMQTextMessage {commandId = 5, ..., jmsXGroupFirstForConsumer = false, text = SalesMessage [sales=[Sale [product_type=produ...ssageType=1]}
SalesMessage salesMessage = (SalesMessage) messageConverter.fromMessage(message); // <<< here is the focus of my question
System.out.println("salesMessage: "+salesMessage);
}
catch(JMSException e) {
e.printStackTrace();
}
}
}
Spring 配置
@Configuration
@ComponentScan
@EnableJms
public class AppConfig {
public static final String QUEUE_NAME = "example.queue";
@Bean
public ConnectionFactory connectionFactory() {
ConnectionFactory connectionFactory =
new ActiveMQConnectionFactory("vm://localhost");
return connectionFactory;
}
@Bean
public JmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
//core poll size=4 threads and max poll size 8 threads
factory.setConcurrency("4-8");
return factory;
}
@Bean
MessageConverter converter(){
return new SimpleMessageConverter();
}
}
销售消息class
public class SalesMessage implements Serializable{
List<Sale> sales = new ArrayList<Sale>();
int messageType;
//getters/setters/toString()
促销 class
public class Sale implements Serializable{
private String product_type;
private Long value;
//getters/setters/toString()
正在发送消息
@Component
public class MyMessageSender {
@Autowired
private ConnectionFactory connectionFactory;
private JmsTemplate jmsTemplate;
@PostConstruct
public void init() {
this.jmsTemplate = new JmsTemplate(connectionFactory);
}
public void sendMessage(String message) {
System.out.println("sending: " + message);
jmsTemplate.send(AppConfig.QUEUE_NAME, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(message);
}
});
}
}
*** 已编辑
Spring 配置已更新
@Configuration
@ComponentScan
@EnableJms
public class AppConfig {
public static final String QUEUE_NAME = "example.queue";
@Bean
public ConnectionFactory connectionFactory() {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://localhost");
return connectionFactory;
}
@Bean
public JmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
// core poll size=4 threads and max poll size 8 threads
factory.setConcurrency("4-8");
return factory;
}
@Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
@Bean
public JmsTemplate jmsTemplate() {
JmsTemplate template = new JmsTemplate();
template.setMessageConverter(jacksonJmsMessageConverter());
template.setConnectionFactory(connectionFactory());
return template;
}
*** 已编辑
当我在 Listener 端打印消息时,我得到
received: ActiveMQObjectMessage {commandId = 5, responseRequired = true, messageId = ID:win10-cha-51561-1509914170598-4:2:1:1:1, originalDestination = null, originalTransactionId = null, producerId = ID:win10-cha-51561-1509914170598-4:2:1:1, destination = queue://example.queue, transactionId = null, expiration = 0, timestamp = 1509914232995, arrival = 0, brokerInTime = 1509914232995, brokerOutTime = 1509914233010, correlationId = null, replyTo = null, persistent = true, type = null, priority = 4, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = org.apache.activemq.util.ByteSequence@24f339bf, marshalledProperties = null, dataStructure = null, redeliveryCounter = 0, size = 1346, properties = null, readOnlyProperties = true, readOnlyBody = true, droppable = false, jmsXGroupFirstForConsumer = false}
和 SalesMessage salesMessage = (SalesMessage) messageConverter.fromMessage(message) 引发此异常
org.springframework.jms.support.converter.MessageConversionException: Could not find type id property [_type] on message [ID:win10-cha-51561-1509914170598-4:2:1:1:1] from destination [queue://example.queue]
在发送端,我打印消息
SalesMessage [sales=[Sale [product_type=product_type, value=1]], messageType=1]
并且,这是我更新的完整应用程序配置(我遵循提供的建议)
@Configuration
@ComponentScan
@EnableJms
public class AppConfig {
public static final String QUEUE_NAME = "example.queue";
@Bean
public ConnectionFactory connectionFactory() {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://localhost");
return connectionFactory;
}
@Bean
public JmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setConcurrency("4-8");
factory.setMessageConverter(jacksonJmsMessageConverter());
return factory;
}
@Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
converter.setObjectMapper(objectMapper);
return converter;
}
}
而且,这是我在上次建议后发送消息的方式(重大变化是从 jmsTemplate.send 到 jmsTemplate.convertAndSend):
@Component
public class MyMessageSender {
@Autowired
private ConnectionFactory connectionFactory;
private JmsTemplate jmsTemplate;
@PostConstruct
public void init() {
this.jmsTemplate = new JmsTemplate(connectionFactory);
}
public void sendMessage(SalesMessage message) {
System.out.println("sending: " + message);
jmsTemplate.convertAndSend(AppConfig.QUEUE_NAME, message);
}
}
*** 已编辑
我明白你在这里想做什么,你可能正在尝试遵循这个例子:
https://spring.io/guides/gs/messaging-jms/
但是,该示例是在 Spring Boot.我有一种预感,你没有使用 Spring 引导,因为那里没有 @SpringBootApplication
注释。
如果我是对的,您需要使用您定义为 bean 的 SimpleMessageConverter 明确调用 DefaultJmsListenerContainerFactory.setMessageConverter 方法。我敢打赌 Spring Boot 可能会自动为您执行此操作,但在您的实现中,您必须手动执行此操作。
所以,它应该看起来像:
@Bean
public JmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setMessageConverter(converter());
factory.setConcurrency("4-8");
return factory;
}
您还需要确保您的对象是可序列化的,然后您可以对对象本身执行 jmsTemplate.send
。转换器将为您进行必要的消息负载类型转换。
嗯?
ms.sendMessage(sm.toString());
java.lang.ClassCastException: java.lang.String cannot be cast to ...SalesMessage.
那是因为您发送的是 String
(sm.toString()
) 而不是 JSON 形式的 SalesMessage
。
使用jmsTemplate.convertAnsSend(destinationName, sm)
.
监听器容器工厂也需要Jackson转换器。
我想发送包含销售列表的 JMS 消息并通过 onMessage()
接收。据我所知,springframework.jms.support.converter.MessageConverter
应该起到转换或转换消息的作用,因此我不必创建 XSD 或额外的映射 classes。我的意思是,我期待下面的测试发送一条消息和 (SalesMessage) messageConverter.fromMessage(message)
足以转换和实例化 SalesMessage
的对象。
基本上,错误是java.lang.ClassCastException: java.lang.String cannot be cast to ...SalesMessage
。
我的测试
AnnotationConfigApplicationContext context;
MyMessageSender ms;
JmsListenerEndpointRegistry bean;
@Before
public void setUpBeforeClass() throws Exception {
context = new AnnotationConfigApplicationContext(AppConfig.class);
ms = context.getBean(MyMessageSender.class);
}
@Test
public void test2() {
Sale s = new Sale("product_type", 1L);
List<Sale> l = new ArrayList<Sale>();
l.add(s);
SalesMessage sm = new SalesMessage(l, 1);
ms.sendMessage(sm.toString());
bean = context.getBean(JmsListenerEndpointRegistry.class);
for (MessageListenerContainer listenerContainer : bean.getListenerContainers()) {
DefaultMessageListenerContainer container = (DefaultMessageListenerContainer) listenerContainer;
container.shutdown();
}
}
MyJmsListener
@Component
public class MyJmsListener {
@Autowired
private MessageConverter messageConverter;
@JmsListener(destination = AppConfig.QUEUE_NAME)
public void handleMessage(Message message) {
try {
System.out.println("received: "+message);
//this prints received: ActiveMQTextMessage {commandId = 5, ..., jmsXGroupFirstForConsumer = false, text = SalesMessage [sales=[Sale [product_type=produ...ssageType=1]}
SalesMessage salesMessage = (SalesMessage) messageConverter.fromMessage(message); // <<< here is the focus of my question
System.out.println("salesMessage: "+salesMessage);
}
catch(JMSException e) {
e.printStackTrace();
}
}
}
Spring 配置
@Configuration
@ComponentScan
@EnableJms
public class AppConfig {
public static final String QUEUE_NAME = "example.queue";
@Bean
public ConnectionFactory connectionFactory() {
ConnectionFactory connectionFactory =
new ActiveMQConnectionFactory("vm://localhost");
return connectionFactory;
}
@Bean
public JmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
//core poll size=4 threads and max poll size 8 threads
factory.setConcurrency("4-8");
return factory;
}
@Bean
MessageConverter converter(){
return new SimpleMessageConverter();
}
}
销售消息class
public class SalesMessage implements Serializable{
List<Sale> sales = new ArrayList<Sale>();
int messageType;
//getters/setters/toString()
促销 class
public class Sale implements Serializable{
private String product_type;
private Long value;
//getters/setters/toString()
正在发送消息
@Component
public class MyMessageSender {
@Autowired
private ConnectionFactory connectionFactory;
private JmsTemplate jmsTemplate;
@PostConstruct
public void init() {
this.jmsTemplate = new JmsTemplate(connectionFactory);
}
public void sendMessage(String message) {
System.out.println("sending: " + message);
jmsTemplate.send(AppConfig.QUEUE_NAME, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(message);
}
});
}
}
*** 已编辑 Spring 配置已更新
@Configuration
@ComponentScan
@EnableJms
public class AppConfig {
public static final String QUEUE_NAME = "example.queue";
@Bean
public ConnectionFactory connectionFactory() {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://localhost");
return connectionFactory;
}
@Bean
public JmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
// core poll size=4 threads and max poll size 8 threads
factory.setConcurrency("4-8");
return factory;
}
@Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
@Bean
public JmsTemplate jmsTemplate() {
JmsTemplate template = new JmsTemplate();
template.setMessageConverter(jacksonJmsMessageConverter());
template.setConnectionFactory(connectionFactory());
return template;
}
*** 已编辑 当我在 Listener 端打印消息时,我得到
received: ActiveMQObjectMessage {commandId = 5, responseRequired = true, messageId = ID:win10-cha-51561-1509914170598-4:2:1:1:1, originalDestination = null, originalTransactionId = null, producerId = ID:win10-cha-51561-1509914170598-4:2:1:1, destination = queue://example.queue, transactionId = null, expiration = 0, timestamp = 1509914232995, arrival = 0, brokerInTime = 1509914232995, brokerOutTime = 1509914233010, correlationId = null, replyTo = null, persistent = true, type = null, priority = 4, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = org.apache.activemq.util.ByteSequence@24f339bf, marshalledProperties = null, dataStructure = null, redeliveryCounter = 0, size = 1346, properties = null, readOnlyProperties = true, readOnlyBody = true, droppable = false, jmsXGroupFirstForConsumer = false}
和 SalesMessage salesMessage = (SalesMessage) messageConverter.fromMessage(message) 引发此异常
org.springframework.jms.support.converter.MessageConversionException: Could not find type id property [_type] on message [ID:win10-cha-51561-1509914170598-4:2:1:1:1] from destination [queue://example.queue]
在发送端,我打印消息
SalesMessage [sales=[Sale [product_type=product_type, value=1]], messageType=1]
并且,这是我更新的完整应用程序配置(我遵循提供的建议)
@Configuration
@ComponentScan
@EnableJms
public class AppConfig {
public static final String QUEUE_NAME = "example.queue";
@Bean
public ConnectionFactory connectionFactory() {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://localhost");
return connectionFactory;
}
@Bean
public JmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setConcurrency("4-8");
factory.setMessageConverter(jacksonJmsMessageConverter());
return factory;
}
@Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
converter.setObjectMapper(objectMapper);
return converter;
}
}
而且,这是我在上次建议后发送消息的方式(重大变化是从 jmsTemplate.send 到 jmsTemplate.convertAndSend):
@Component
public class MyMessageSender {
@Autowired
private ConnectionFactory connectionFactory;
private JmsTemplate jmsTemplate;
@PostConstruct
public void init() {
this.jmsTemplate = new JmsTemplate(connectionFactory);
}
public void sendMessage(SalesMessage message) {
System.out.println("sending: " + message);
jmsTemplate.convertAndSend(AppConfig.QUEUE_NAME, message);
}
}
*** 已编辑
我明白你在这里想做什么,你可能正在尝试遵循这个例子:
https://spring.io/guides/gs/messaging-jms/
但是,该示例是在 Spring Boot.我有一种预感,你没有使用 Spring 引导,因为那里没有 @SpringBootApplication
注释。
如果我是对的,您需要使用您定义为 bean 的 SimpleMessageConverter 明确调用 DefaultJmsListenerContainerFactory.setMessageConverter 方法。我敢打赌 Spring Boot 可能会自动为您执行此操作,但在您的实现中,您必须手动执行此操作。
所以,它应该看起来像:
@Bean
public JmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setMessageConverter(converter());
factory.setConcurrency("4-8");
return factory;
}
您还需要确保您的对象是可序列化的,然后您可以对对象本身执行 jmsTemplate.send
。转换器将为您进行必要的消息负载类型转换。
嗯?
ms.sendMessage(sm.toString());
java.lang.ClassCastException: java.lang.String cannot be cast to ...SalesMessage.
那是因为您发送的是 String
(sm.toString()
) 而不是 JSON 形式的 SalesMessage
。
使用jmsTemplate.convertAnsSend(destinationName, sm)
.
监听器容器工厂也需要Jackson转换器。