【问题标题】:Cannot convert object of type to JMS message. Supported message payloads are: String, byte array, Map<String,?>, Serializable object无法将类型的对象转换为 JMS 消息。支持的消息有效负载有:字符串、字节数组、Map<String,?>、Serializable 对象
【发布时间】:2019-10-22 00:57:02
【问题描述】:

我正在开发 Spring + ActiveMQ + JMS 示例。在此示例中,我面临以下错误:我尝试了许多选项,但根本不起作用。

我希望实现以下功能:

1) 队列应该一直在读取消息(使用转换器或侦听器)

2) 根据 InstructionMessage 类型,我必须决定将其发送到哪里。

代码上传于:https://github.com/test512/spring-mvc-jms-tutorials

Sending person InstructionMessage [instructionType=10, productCode=10, quantity=10, uOM=10, timeStamp=10]
Exception in thread "main" org.springframework.jms.support.converter.MessageConversionException: Cannot convert object of type [com.jms.testing.spring.InstructionMessage] to JMS message. Supported message payloads are: String, byte array, Map<String,?>, Serializable object.
    at org.springframework.jms.support.converter.SimpleMessageConverter.toMessage(SimpleMessageConverter.java:78)
    at org.springframework.jms.core.JmsTemplate$5.createMessage(JmsTemplate.java:651)
    at org.springframework.jms.core.JmsTemplate.doSend(JmsTemplate.java:593)
    at org.springframework.jms.core.JmsTemplate$3.doInJms(JmsTemplate.java:562)
    at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:484)
    at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:559)
    at org.springframework.jms.core.JmsTemplate.convertAndSend(JmsTemplate.java:648)
    at org.springframework.jms.core.JmsTemplate.convertAndSend(JmsTemplate.java:639)
    at com.jms.testing.spring.SpringJmsPersonProducer.sendMessage(SpringJmsPersonProducer.java:18)
    at com.jms.testing.spring.SpringJmsMessageConverterExample.main(SpringJmsMessageConverterExample.java:16)

appContextWithMessageConverter.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jms="http://www.springframework.org/schema/jms"
    xsi:schemaLocation="http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:property-placeholder location="classpath:jms.properties" />

    <bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="${jms.brokerURL}" />
    </bean>

    <bean id="pooledJmsConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
        <property name="connectionFactory" ref="jmsConnectionFactory" />
        <property name="maxConnections" value="50" />
    </bean>

    <jms:listener-container container-type="default" connection-factory="pooledJmsConnectionFactory" acknowledge="auto" >
        <!-- <jms:listener destination="messageDestination" ref="messageDestination" /> -->
        <jms:listener destination="messageDestination" ref="myListener" />
    </jms:listener-container>

    <bean id="instructionMessageConverter" class="com.jms.testing.spring.InstructionMessageConverter" />

    <bean id="myListener" class="com.jms.testing.spring.MyListener" />

    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <constructor-arg ref="pooledJmsConnectionFactory" />
        <property name="defaultDestination" ref="messageDestination" />
        <property name="receiveTimeout" value="${jms.receiveTimeout}" />
        <!-- <property name="messageConverter" ref="instructionMessageConverter" /> -->

    </bean>

    <bean id="springJmsPersonProducer" class="com.jms.testing.spring.SpringJmsPersonProducer">
        <property name="jmsTemplate" ref="jmsTemplate" />
    </bean>

    <bean id="springJmsPersonConsumer" class="com.jms.testing.spring.SpringJmsPersonConsumer">
        <property name="jmsTemplate" ref="jmsTemplate" />
    </bean> 

    <bean id="messageDestination" class="org.apache.activemq.command.ActiveMQQueue">
        <constructor-arg value="messageQueue1" />
    </bean>
</beans>

MyListener.java

public class MyListener implements MessageListener {

    @Override
    public void onMessage(Message message) {
        MapMessage mapMessage = (MapMessage) message;

        try {
            int instructionType = Integer.parseInt(mapMessage.getString("instructionType"));
            int productCode = Integer.parseInt(mapMessage.getString("productCode"));
            int quantity = Integer.parseInt(mapMessage.getString("quantity"));
            int timeStamp = Integer.parseInt(mapMessage.getString("timeStamp"));
            int uOM = Integer.parseInt(mapMessage.getString("uOM"));
            InstructionMessage instructionMessage = new InstructionMessage(instructionType, productCode, quantity, uOM,
                    timeStamp);
            System.out.println(instructionMessage.toString());

        } catch (NumberFormatException | JMSException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

InstructionMessage.java

public class InstructionMessage implements Serializable{

private static final long serialVersionUID = 1L;
    private int instructionType;
    private int productCode;
    private int quantity;
    private int uOM;
    private int timeStamp;


    public InstructionMessage(int instructionType, int productCode, int quantity, int uOM, int timeStamp) {
        super();
        this.instructionType = instructionType;
        this.productCode = productCode;
        this.quantity = quantity;
        this.uOM = uOM;
        this.timeStamp = timeStamp;
    }

    public int getInstructionType() {
        return instructionType;
    }

    public void setInstructionType(int instructionType) {
        this.instructionType = instructionType;
    }

    public int getProductCode() {
        return productCode;
    }

    public void setProductCode(int productCode) {
        this.productCode = productCode;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public int getuOM() {
        return uOM;
    }

    public void setuOM(int uOM) {
        this.uOM = uOM;
    }

    public int getTimeStamp() {
        return timeStamp;
    }

    public void setTimeStamp(int timeStamp) {
        this.timeStamp = timeStamp;
    }

    @Override
    public String toString() {
        return "InstructionMessage [instructionType=" + instructionType + ", productCode=" + productCode + ", quantity="
                + quantity + ", uOM=" + uOM + ", timeStamp=" + timeStamp + "]";
    }
}

SpringJmsMessageConverterExample.java

public class SpringJmsMessageConverterExample {
    public static void main(String[] args) throws URISyntaxException, Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                "appContextWithMessageConverter.xml");

        try {
            SpringJmsPersonProducer springJmsProducer = (SpringJmsPersonProducer) context.getBean("springJmsPersonProducer");
            InstructionMessage m1 = new InstructionMessage(10,10,10,10,10);
            System.out.println("Sending person " + m1);
            springJmsProducer.sendMessage(m1);

            InstructionMessage m2 = new InstructionMessage(5,5,5,5,5);
            System.out.println("Sending person " + m2);
            springJmsProducer.sendMessage(m2);

            InstructionMessage m3 = new InstructionMessage(0,0,0,0,0);
            System.out.println("Sending person " + m3);
            springJmsProducer.sendMessage(m3);

            System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

            SpringJmsPersonConsumer springJmsConsumer = (SpringJmsPersonConsumer) context.getBean("springJmsPersonConsumer");
            System.out.println("Consumer receives " + springJmsConsumer.receiveMessage());
        } finally {
            context.close();
        }
    }
}

SpringJmsPersonProducer.java

public class SpringJmsPersonProducer {

    private JmsTemplate jmsTemplate;

    public JmsTemplate getJmsTemplate() {
        return jmsTemplate;
    }

    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public void sendMessage(final InstructionMessage instructionMessage) {
        getJmsTemplate().convertAndSend(instructionMessage);
    }
}

SpringJmsPersonConsumer.java

public class SpringJmsPersonConsumer {

    private JmsTemplate jmsTemplate;

    public JmsTemplate getJmsTemplate() {
        return jmsTemplate;
    }

    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public InstructionMessage receiveMessage() throws JMSException {
        InstructionMessage instructionMessage = (InstructionMessage) getJmsTemplate().receiveAndConvert();
        return instructionMessage;  
    }
}

【问题讨论】:

    标签: java jms activemq


    【解决方案1】:

    改为使用 Jackson 转换,在需要触发多个事件对象的事件驱动系统中很方便

    @Bean
    public JmsTemplate jmsTemplate() {
        JmsTemplate template = new JmsTemplate();
        template.setConnectionFactory(connectionFactory());
        template.setMessageConverter(jacksonJmsMessageConverter());
        template.setPubSubDomain(true);
        return template;
    }
    
    @Bean
    public MessageConverter jacksonJmsMessageConverter() {
        MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
        converter.setTargetType(MessageType.TEXT);
        converter.setTypeIdPropertyName("_type");
        return converter;
    }
    

    然后设置

    containerFactory.setMessageConverter(jacksonJmsMessageConverter());

    就是这样。

    【讨论】:

    • template.setMessageConverter(jacksonJmsMessageConverter()); 这个赋值应该足以在发送消息之前在消息转换期间将对象转换为文本类型。我觉得不用再把这个messageConverter赋值给containerFactory了吧?
    【解决方案2】:

    只需添加生成的序列版本 ID 而不是默认的 1L!

    public class InstructionMessage implements Serializable{
    
    private static final long serialVersionUID = -295422703255886286L;
    

    更新:

    1) 为了让您的侦听器正常工作,您需要更新 destination="messageQueue1 因为使用 messageDestination 容器创建此队列并将您的消息发送到 messageQueue1

    <jms:listener-container container-type="default"
        connection-factory="pooledJmsConnectionFactory" acknowledge="auto">
        <jms:listener destination="messageQueue1" ref="myListener" />
    </jms:listener-container>
    

    并更新 MyListener

    import javax.jms.JMSException;
    import javax.jms.Message;
    import javax.jms.MessageListener;
    import javax.jms.ObjectMessage;
    
    public class MyListener implements MessageListener {
    
        @Override
        public void onMessage(Message message) {
            try {
                ObjectMessage mapMessage = (ObjectMessage) message;
                InstructionMessage instructionMessage = (InstructionMessage) mapMessage.getObject();
                System.out.println(instructionMessage.toString());
            } catch (NumberFormatException | JMSException e) {
                e.printStackTrace();
            }
        }
    }
    

    2) 有条件发送

    import org.springframework.jms.core.JmsTemplate;
    
    public class SpringJmsPersonProducer {
    
        private JmsTemplate jmsTemplate;
    
        public JmsTemplate getJmsTemplate() {
            return jmsTemplate;
        }
    
        public void setJmsTemplate(JmsTemplate jmsTemplate) {
            this.jmsTemplate = jmsTemplate;
        }
    
        public void sendMessage(final InstructionMessage instructionMessage) {
            if (canSend(instructionMessage)) {
                getJmsTemplate().convertAndSend(instructionMessage);
            } else {
                throw new IllegalArgumentException("message");
            }
        }
    
        private boolean canSend(InstructionMessage instructionMessage) {
            return instructionMessage.getQuantity() > 0;
        }
    }
    

    【讨论】:

    • 我已经测试了你的 github 代码,它与生成的序列版本 ID 一起工作正常,你试过了吗?
    • 只生成serialVersionUID = -295422703255886286L;因为默认 1L 我有例外
    • 您清除队列了吗?不是因为旧消息??
    • 是的,我删除/清除队列中的所有旧消息,并且工作正常。谢谢你。但仍然是我的未解决问题 1)和 2)
    • 我的听众没有打电话我想做 1) 队列应该继续阅读消息(使用转换器或听众) 2) 基于 InstructionMessage 类型,我必须决定将它发送到哪里或不是。
    【解决方案3】:

    JMS 将数据移动到不同的 JVM。不能用普通的物体来完成。必须序列化,所以需要实现接口 Serializable 假设可以这样做,请询问主要架构师

    import java.io.Serializable;
    ...
    public class InstructionMessage implements Serializable {
    
     private static final long serialVersionUID = 7526472295622776147L;
    
    ....
    }
    

    背景:网络不理解Java对象,只能传输字节。 它是 Java 开发的基础,任何书都可以提供帮助(Eckel 的 Thinking in Java)

    编辑:部分回答(尝试回答)您更改了(使用Serializable)代码。 现在ClassCastException 异常说:发送和接收部分相互不兼容。 框架或服务器进行了转换 InstructionMessage -> HashMap,我猜是在早期阶段。

    我也猜,但在这里我几乎可以肯定,HashMap 的内容在功能上是相同的。

    您最简单但不优雅的方法是离开 InstructionMessage 并在两侧使用 HashMap(更改发件人),或调试问题。

    如果我记得,您删除了 Converter 类 java,但仍保留在 XML 中。我不是 Spring 的忠实粉丝(个人讨厌“xml 编程”),我想 XML 中的一些修正会有所帮助

    【讨论】:

    • 我收到错误:Exception in thread "main" java.lang.ClassCastException: java.util.HashMap cannot be cast to com.jms.testing.spring.InstructionMessage at com.jms.testing.spring.SpringJmsPersonConsumer.receiveMessage(SpringJmsPersonConsumer.java:20) at com.jms.testing.spring.SpringJmsMessageConverterExample.main(SpringJmsMessageConverterExample.java:29)
    • 接收方以字节为单位假设流是 HashMap(一种能够移动数据的通用结构,通常适用于 JMS),但以字节为单位的是 InstructionMessage 。发送方和接收方不兼容。让我看看代码中的一些时间
    • 嘿,我上传了我的代码github.com/test512/spring-mvc-jms-tutorials。您只需要下载 ActiveMQ 即可对其进行测试。感谢您的陪伴。
    • 嗨!我必须在某个地方停下来,抱歉……我不是 Spring 大师。我敢肯定,您可以在 HashMap 上构建整个解决方案(如果无法等待 sping-people)
    猜你喜欢
    • 2014-11-03
    • 1970-01-01
    • 1970-01-01
    • 2013-12-13
    • 1970-01-01
    • 2014-09-07
    • 1970-01-01
    • 1970-01-01
    • 2016-01-15
    相关资源
    最近更新 更多