请选择 进入手机版 | 继续访问电脑版
设为首页 收藏本站
开启辅助访问 切换到宽版

新浪微博登陆

只需一步, 快速开始

QQ登录

只需一步,快速开始

切换风格 立即注册 找回密码

Java教程网

新浪微博达人勋

1589

积分

933

帖子

93

贡献

Rank: 6Rank: 6

积分
1589

社区QQ达人最佳新人热心会员推广达人

发表于 2018-7-27 19:51:57 | 显示全部楼层 |阅读模式
1.4     消息转换器MessageConverter
MessageConverter的作用主要有两方面,一方面它可以把我们的非标准化Message对象转换成我们的目标Message对象,这主要是用在发送消息的时候;另一方面它又可以把我们的Message对象转换成对应的目标对象,这主要是用在接收消息的时候。
下面我们就拿发送一个对象消息来举例,假设我们有这样一个需求:我们平台有一个发送邮件的功能,进行发送的时候我们只是把我们的相关信息封装成一个JMS消息,然后利用JMS进行发送,在对应的消息监听器进行接收到的消息处理时才真正的进行消息发送。
假设我们有这么一个Email对象:
  1. public class Email implements Serializable {
  2.     private static final long serialVersionUID = -658250125732806493L;
  3.     private String receiver;
  4.     private String title;
  5.     private String content;
  6.     public Email(String receiver, String title, String content) {
  7.         this.receiver = receiver;
  8.         this.title = title;
  9.         this.content = content;
  10.     }

  11.     public String getReceiver() {
  12.         return receiver;
  13.     }

  14.     public void setReceiver(String receiver) {
  15.         this.receiver = receiver;
  16.     }

  17.     public String getTitle() {
  18.         return title;
  19.     }

  20.     public void setTitle(String title) {
  21.         this.title = title;
  22.     }

  23.     public String getContent() {
  24.         return content;
  25.     }

  26.     public void setContent(String content) {
  27.         this.content = content;
  28.     }

  29.     @Override
  30.     public String toString() {
  31.         StringBuilder builder = new StringBuilder();
  32.         builder.append("Email [receiver=").append(receiver).append(", title=")
  33.                 .append(title).append(", content=").append(content).append("]");
  34.         return builder.toString();
  35.     }
  36.    
  37. }
复制代码
这个Email对象包含了一个简单的接收者email地址、邮件主题和邮件内容。我们在发送的时候就把这个对象封装成一个ObjectMessage进行发送。代码如下所示:
  1. public class ProducerServiceImpl implements ProducerService {
  2.     @Autowired
  3.     private JmsTemplate jmsTemplate;
  4.     public void sendMessage(Destination destination, final Serializable obj) {
  5.         jmsTemplate.send(destination, new MessageCreator() {
  6.             public Message createMessage(Session session) throws JMSException {
  7.                 ObjectMessage objMessage = session.createObjectMessage(obj);
  8.                 return objMessage;
  9.             }
  10.         });
  11.     }
  12. }
复制代码
这是对应的在没有使用MessageConverter的时候我们需要new一个MessageCreator接口对象,然后在其抽象方法createMessage内部使用session创建一个对应的消息。在使用了MessageConverter的时候我们在使用JmsTemplate进行消息发送时只需要调用其对应的convertAndSend方法即可
  1.     public void sendMessage(Destination destination, final Serializable obj) {
  2.         //未使用MessageConverter的情况
  3.         /*jmsTemplate.send(destination, new MessageCreator() {

  4.             public Message createMessage(Session session) throws JMSException {
  5.                 ObjectMessage objMessage = session.createObjectMessage(obj);
  6.                 return objMessage;
  7.             }
  8.             
  9.         });*/
  10.         //使用MessageConverter的情况
  11.         jmsTemplate.convertAndSend(destination, obj);
  12.     }
复制代码

这样JmsTemplate就会在其内部调用预定的MessageConverter对我们的消息对象进行转换,然后再进行发送。
       这个时候我们就需要定义我们的MessageConverter了。要定义自己的MessageConverter很简单,只需要实现Spring为我们提供的MessageConverter接口即可。我们先来看一下MessageConverter接口的定义:
  1. public interface MessageConverter {
  2.     Message toMessage(Object object, Session session) throws JMSException, MessageConversionException;
  3.     Object fromMessage(Message message) throws JMSException, MessageConversionException;
  4. }
复制代码
我们可以看到其中一共定义了两个方法fromMessage和toMessage,fromMessage是用来把一个JMS Message转换成对应的Java对象,而toMessage方法是用来把一个Java对象转换成对应的JMS Message。因为我们已经知道上面要发送的对象就是一个Email对象,所以在这里我们就简单地定义一个EmailMessageConverter用来把Email对象和对应的ObjectMessage进行转换
  1. import javax.jms.JMSException;
  2. import javax.jms.Message;
  3. import javax.jms.ObjectMessage;
  4. import javax.jms.Session;

  5. import org.springframework.jms.support.converter.MessageConversionException;
  6. import org.springframework.jms.support.converter.MessageConverter;

  7. public class EmailMessageConverter implements MessageConverter {

  8.     public Message toMessage(Object object, Session session)
  9.             throws JMSException, MessageConversionException {
  10.         return session.createObjectMessage((Serializable) object);
  11.     }

  12.     public Object fromMessage(Message message) throws JMSException,
  13.             MessageConversionException {
  14.         ObjectMessage objMessage = (ObjectMessage) message;
  15.         return objMessage.getObject();
  16.     }

  17. }
复制代码
这样当我们利用JmsTemplate的convertAndSend方法发送一个Email对象的时候就会把对应的Email对象当做参数调用我们定义好的EmailMessageConverter的toMessage方法。
       定义好我们的EmailMessageConverter之后就需要指定我们用来发送Email对象的JmsTemplate对象的messageConverter为EmailMessageConverter,这里我们在Spring的配置文件中定义JmsTemplate bean的时候就指定:
  1.     <!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 -->
  2.     <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
  3.         <!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->
  4.         <property name="connectionFactory" ref="connectionFactory"/>
  5.         <!-- 消息转换器 -->
  6.         <property name="messageConverter" ref="emailMessageConverter"/>
  7.     </bean>
  8.     <!-- 类型转换器 -->
  9.     <bean id="emailMessageConverter" class="com.tiantian.springintejms.converter.EmailMessageConverter"/>
复制代码
到此我们的MessageConverter就定义好了,也能够进行使用了,接着我们来进行测试一下,定义测试代码如下所示:
  1.     @Test
  2.     public void testObjectMessage() {
  3.         Email email = new Email("zhangsan@xxx.com", "主题", "内容");
  4.         producerService.sendMessage(destination, email);
  5.     }
复制代码
上面destination对应的接收处理的MessageListener方法如下所示:
  1. public class ConsumerMessageListener implements MessageListener {

  2.     public void onMessage(Message message) {
  3.         
  4.         if (message instanceof ObjectMessage) {
  5.             ObjectMessage objMessage = (ObjectMessage) message;
  6.             try {
  7.                 Object obj = objMessage.getObject();
  8.                 Email email = (Email) obj;
  9.                 System.out.println("接收到一个ObjectMessage,包含Email对象。");
  10.                 System.out.println(email);
  11.             } catch (JMSException e) {
  12.                 e.printStackTrace();
  13.             }
  14.         }
  15.     }
  16. }
复制代码
之前说了MessageConverter有两方面的功能,除了把Java对象转换成对应的Jms Message之外还可以把Jms Message转换成对应的Java对象。我们看上面的消息监听器在接收消息的时候接收到的就是一个Jms Message,如果我们要利用MessageConverter来把它转换成对应的Java对象的话,只能是我们往里面注入一个对应的MessageConverter,然后在里面手动的调用,如:
  1. public class ConsumerMessageListener implements MessageListener {
  2.     private MessageConverter messageConverter;
  3.     public void onMessage(Message message) {
  4.         if (message instanceof ObjectMessage) {
  5.             ObjectMessage objMessage = (ObjectMessage) message;
  6.             try {
  7.                 /*Object obj = objMessage.getObject();
  8.                 Email email = (Email) obj;*/
  9.                 Email email = (Email) messageConverter.fromMessage(objMessage);
  10.                 System.out.println("接收到一个ObjectMessage,包含Email对象。");
  11.                 System.out.println(email);
  12.             } catch (JMSException e) {
  13.                 e.printStackTrace();
  14.             }
  15.         }
  16.     }
  17.     public MessageConverter getMessageConverter() {
  18.         return messageConverter;
  19.     }
  20.     public void setMessageConverter(MessageConverter messageConverter) {
  21.         this.messageConverter = messageConverter;
  22.     }
  23. }
复制代码
当我们使用MessageListenerAdapter来作为消息监听器的时候,我们可以为它指定一个对应的MessageConverter,这样Spring在处理接收到的消息的时候就会自动地利用我们指定的MessageConverter对它进行转换,然后把转换后的Java对象作为参数调用指定的消息处理方法。这里我们再把前面讲解MessageListenerAdapter时定义的MessageListenerAdapter拿来做一个测试,我们指定它的MessageConverter为我们定义好的EmailMessageConverter。
  1.     <!-- 消息监听适配器 -->
  2.     <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
  3.         <property name="delegate">
  4.             <bean class="com.tiantian.springintejms.listener.ConsumerListener"/>
  5.         </property>
  6.         <property name="defaultListenerMethod" value="receiveMessage"/>
  7.         <property name="messageConverter" ref="emailMessageConverter"/>
  8.     </bean>
  9.     <!-- 消息监听适配器对应的监听容器 -->
  10.     <bean id="messageListenerAdapterContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
  11.         <property name="connectionFactory" ref="connectionFactory"/>
  12.         <property name="destination" ref="adapterQueue"/>
  13.         <property name="messageListener" ref="messageListenerAdapter"/><!-- 使用MessageListenerAdapter来作为消息监听器 -->
  14.     </bean>
复制代码
然后在我们的真正用于处理接收到的消息的ConsumerListener中添加一个receiveMessage方法,添加后其代码如下所示:
  1. public class ConsumerListener {

  2.     public void receiveMessage(String message) {
  3.         System.out.println("ConsumerListener通过receiveMessage接收到一个纯文本消息,消息内容是:" + message);
  4.     }
  5.    
  6.     public void receiveMessage(Email email) {
  7.         System.out.println("接收到一个包含Email的ObjectMessage。");
  8.         System.out.println(email);
  9.     }
  10. }
复制代码
  然后我们定义如下测试代码:
  1.     @Test
  2.     public void testObjectMessage() {
  3.         Email email = new Email("zhangsan@xxx.com", "主题", "内容");
  4.         producerService.sendMessage(adapterQueue, email);
  5.     }
复制代码
因为我们给MessageListenerAdapter指定了一个MessageConverter,而且是一个EmailMessageConverter,所以当MessageListenerAdapter接收到一个消息后,它会调用我们指定的MessageConverter的fromMessage方法把它转换成一个Java对象,根据定义这里会转换成一个Email对象,然后会把这个Email对象作为参数调用我们通过defaultListenerMethod属性指定的默认处理器方法,根据定义这里就是receiveMessage方法,但是我们可以看到在ConsumerListener中我们一共定义了两个receiveMessage方法,因为是通过转换后的Email对象作为参数进行方法调用的,所以这里调用的就应该是参数类型为Email的receiveMessage方法了。上述测试代码运行后会输出如下结果:
ee3a2dc6-82ce-3d4c-a7a5-30f5adca1499.jpg
        说到这里可能有读者就会有疑问了,说我们在之前讲解MessageListenerAdapter的时候不是没有指定对应的MessageConverter,然后发送了一个TextMessage,结果Spring还是把它转换成一个String对象,调用了ConsumerListener参数类型为String的receiveMessage方法吗?那你这个MessageConverter在MessageListenerAdapter进行消息接收的时候也没什么用啊。
       其实还是有用的,在我们使用MessageListenerAdapter时,在对其进行初始化也就是调用其构造方法时,它会默认new一个Spring已经为我们实现了的MessageConverter——SimpleMessageConverter作为其默认的MessageConverter,这也就是为什么我们在使用MessageListenerAdapter的时候不需要指定MessageConverter但是消息还是会转换成对应的Java对象的原因。所以默认情况下我们使用MessageListenerAdapter时其对应的MessageListener的处理器方法参数类型必须是一个普通Java对象,而不能是对应的Jms Message对象。
       那如果我们在处理Jms Message的时候想使用MessageListenerAdapter,然后又希望处理最原始的Message,而不是经过MessageConverter进行转换后的Message该怎么办呢?这个时候我们只需要在定义MessageListenerAdapter的时候指定其MessageConverter为空就可以了。
  1.     <!-- 消息监听适配器 -->
  2.     <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
  3.         <property name="delegate">
  4.             <bean class="com.tiantian.springintejms.listener.ConsumerListener"/>
  5.         </property>
  6.         <property name="defaultListenerMethod" value="receiveMessage"/>
  7.         <property name="messageConverter">
  8.             <null/>
  9.         </property>
  10.     </bean>
复制代码
那么这个时候我们的真实MessageListener的处理器方法参数类型就应该是Jms Message或对应的Jms Message子类型了,不然就会调用不到对应的处理方法了。这里因为我们发送的是一个ObjectMessage,所以这里就添加一个对应的参数类型为ObjectMessage的receiveMessage方法了。
  1.     public void receiveMessage(ObjectMessage message) throws JMSException {
  2.         System.out.println(message.getObject());
  3.     }
复制代码
刚刚讲到Spring已经为我们实现了一个简单的MessageConverter,即org.springframework.jms.support.converter.SimpleMessageConverter,其实Spring在初始化JmsTemplate的时候也指定了其对应的MessageConverter为一个SimpleMessageConverter,所以如果我们平常没有什么特殊要求的时候可以直接使用JmsTemplate的convertAndSend系列方法进行消息发送,而不必繁琐的在调用send方法时自己new一个MessageCreator进行相应Message的创建。
这里我们也来看一下SimpleMessageConverter的定义,如果觉得它不能满足你的要求,那我们可以对它里面的部分方法进行重写,或者是完全实现自己的MessageConverter。
  1. public class SimpleMessageConverter implements MessageConverter {

  2.     public Message toMessage(Object object, Session session) throws JMSException, MessageConversionException {
  3.         if (object instanceof Message) {
  4.             return (Message) object;
  5.         }
  6.         else if (object instanceof String) {
  7.             return createMessageForString((String) object, session);
  8.         }
  9.         else if (object instanceof byte[]) {
  10.             return createMessageForByteArray((byte[]) object, session);
  11.         }
  12.         else if (object instanceof Map) {
  13.             return createMessageForMap((Map) object, session);
  14.         }
  15.         else if (object instanceof Serializable) {
  16.             return createMessageForSerializable(((Serializable) object), session);
  17.         }

  18.         else {
  19.             throw new MessageConversionException("Cannot convert object of type [" +
  20.                     ObjectUtils.nullSafeClassName(object) + "] to JMS message. Supported message " +
  21.                     "payloads are: String, byte array, Map<String,?>, Serializable object.");
  22.         }
  23.     }

  24.     public Object fromMessage(Message message) throws JMSException, MessageConversionException {
  25.         if (message instanceof TextMessage) {
  26.             return extractStringFromMessage((TextMessage) message);
  27.         }
  28.         else if (message instanceof BytesMessage) {
  29.             return extractByteArrayFromMessage((BytesMessage) message);
  30.         }
  31.         else if (message instanceof MapMessage) {
  32.             return extractMapFromMessage((MapMessage) message);
  33.         }
  34.         else if (message instanceof ObjectMessage) {
  35.             return extractSerializableFromMessage((ObjectMessage) message);
  36.         }
  37.         else {
  38.             return message;
  39.         }
  40.     }

  41.     protected TextMessage createMessageForString(String text, Session session) throws JMSException {
  42.         return session.createTextMessage(text);
  43.     }

  44.     protected BytesMessage createMessageForByteArray(byte[] bytes, Session session) throws JMSException {
  45.         BytesMessage message = session.createBytesMessage();
  46.         message.writeBytes(bytes);
  47.         return message;
  48.     }

  49.     protected MapMessage createMessageForMap(Map<?, ?> map, Session session) throws JMSException {
  50.         MapMessage message = session.createMapMessage();
  51.         for (Map.Entry entry : map.entrySet()) {
  52.             if (!(entry.getKey() instanceof String)) {
  53.                 throw new MessageConversionException("Cannot convert non-String key of type [" +
  54.                         ObjectUtils.nullSafeClassName(entry.getKey()) + "] to JMS MapMessage entry");
  55.             }
  56.             message.setObject((String) entry.getKey(), entry.getValue());
  57.         }
  58.         return message;
  59.     }

  60.     protected ObjectMessage createMessageForSerializable(Serializable object, Session session) throws JMSException {
  61.         return session.createObjectMessage(object);
  62.     }


  63.     protected String extractStringFromMessage(TextMessage message) throws JMSException {
  64.         return message.getText();
  65.     }

  66.     protected byte[] extractByteArrayFromMessage(BytesMessage message) throws JMSException {
  67.         byte[] bytes = new byte[(int) message.getBodyLength()];
  68.         message.readBytes(bytes);
  69.         return bytes;
  70.     }

  71.     protected Map extractMapFromMessage(MapMessage message) throws JMSException {
  72.         Map<String, Object> map = new HashMap<String, Object>();
  73.         Enumeration en = message.getMapNames();
  74.         while (en.hasMoreElements()) {
  75.             String key = (String) en.nextElement();
  76.             map.put(key, message.getObject(key));
  77.         }
  78.         return map;
  79.     }

  80.     protected Serializable extractSerializableFromMessage(ObjectMessage message) throws JMSException {
  81.         return message.getObject();
  82.     }

  83. }
复制代码

















来自群组: java开发组

新浪微博达人勋

7013

积分

1809

帖子

1713

贡献

Rank: 8Rank: 8

积分
7013

社区QQ达人最佳新人活跃会员热心会员灌水之王突出贡献

发表于 2018-7-27 19:52:33 | 显示全部楼层
发发呆,回回帖,工作结束~

新浪微博达人勋

1888

积分

387

帖子

387

贡献

Rank: 6Rank: 6

积分
1888

社区QQ达人

发表于 2018-7-28 10:07:11 | 显示全部楼层
太棒了,感谢楼主

新浪微博达人勋

2796

积分

699

帖子

699

贡献

Rank: 6Rank: 6

积分
2796
发表于 2018-8-1 03:42:11 | 显示全部楼层
看起来不错

新浪微博达人勋

6196

积分

1549

帖子

1549

贡献

Rank: 8Rank: 8

积分
6196
发表于 2018-8-6 08:48:48 | 显示全部楼层
锄禾日当午,发帖真辛苦。谁知坛中餐,帖帖皆辛苦!

新浪微博达人勋

2854

积分

715

帖子

713

贡献

Rank: 6Rank: 6

积分
2854

社区QQ达人

发表于 2018-8-9 09:04:43 | 显示全部楼层
好,很好,非常好!

新浪微博达人勋

8066

积分

2231

帖子

1715

贡献

Rank: 8Rank: 8

积分
8066

社区QQ达人最佳新人活跃会员热心会员推广达人宣传达人论坛元老

发表于 2018-8-10 04:47:45 | 显示全部楼层
广告位,,坐下看看

新浪微博达人勋

2812

积分

703

帖子

703

贡献

Rank: 6Rank: 6

积分
2812
发表于 2018-9-7 22:18:58 | 显示全部楼层
回个帖子,下班咯~

新浪微博达人勋

6462

积分

1614

帖子

1616

贡献

Rank: 8Rank: 8

积分
6462
发表于 2018-9-8 09:09:47 | 显示全部楼层
前排支持下

新浪微博达人勋

6311

积分

1577

帖子

1578

贡献

Rank: 8Rank: 8

积分
6311

社区QQ达人

发表于 2018-9-9 20:29:30 | 显示全部楼层
学习下
您需要登录后才可以回帖 登录 | 立即注册 新浪微博登陆

本版积分规则

关闭

站长推荐 上一条 /1 下一条

小黑屋|手机版|Archiver|Java教程网    

GMT+8, 2018-10-24 11:51 , Processed in 0.421875 second(s), 45 queries .

Powered by Discuz X3.2

© 2001-2013 JAVA教程网

快速回复 返回顶部 返回列表