Spring学习总结(11)——Spring JMS MessageConverter介绍

消息转换器MessageConverter

MessageConverter的作用主要有两方面,一方面它可以把我们的非标准化Message对象转换成我们的目标Message对象,这主要是用在发送消息的时候;另一方面它又可以把我们的Message对象转换成对应的目标对象,这主要是用在接收消息的时候。

下面我们就拿发送一个对象消息来举例,假设我们有这样一个需求:我们平台有一个发送邮件的功能,进行发送的时候我们只是把我们的相关信息封装成一个JMS消息,然后利用JMS进行发送,在对应的消息监听器进行接收到的消息处理时才真正的进行消息发送。

假设我们有这么一个Email对象:

Java代码  

  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. }

这个Email对象包含了一个简单的接收者email地址、邮件主题和邮件内容。我们在发送的时候就把这个对象封装成一个ObjectMessage进行发送。代码如下所示:

Java代码  

  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方法即可。如:

Java代码  

  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. //使用MessageConverter的情况
  10. jmsTemplate.convertAndSend(destination, obj);
  11. }

这样JmsTemplate就会在其内部调用预定的MessageConverter对我们的消息对象进行转换,然后再进行发送。

这个时候我们就需要定义我们的MessageConverter了。要定义自己的MessageConverter很简单,只需要实现spring为我们提供的MessageConverter接口即可。我们先来看一下MessageConverter接口的定义:

Java代码  

  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进行转换,其代码如下:

Java代码  

  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的时候就指定:

Xml代码  

  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就定义好了,也能够进行使用了,接着我们来进行测试一下,定义测试代码如下所示:

Java代码  

  1. @Test
  2. public void testObjectMessage() {
  3. Email email = new Email("[email protected]", "主题", "内容");
  4. producerService.sendMessage(destination, email);
  5. }

上面destination对应的接收处理的MessageListener方法如下所示:

Java代码  

  1. public class ConsumerMessageListener implements MessageListener {
  2. public void onMessage(Message message) {
  3. if (message instanceof ObjectMessage) {
  4. ObjectMessage objMessage = (ObjectMessage) message;
  5. try {
  6. Object obj = objMessage.getObject();
  7. Email email = (Email) obj;
  8. System.out.println("接收到一个ObjectMessage,包含Email对象。");
  9. System.out.println(email);
  10. catch (JMSException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. }
  15. }

之前说了MessageConverter有两方面的功能,除了把Java对象转换成对应的Jms Message之外还可以把Jms Message转换成对应的Java对象。我们看上面的消息监听器在接收消息的时候接收到的就是一个Jms Message,如果我们要利用MessageConverter来把它转换成对应的Java对象的话,只能是我们往里面注入一个对应的MessageConverter,然后在里面手动的调用,如:

Java代码  

  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。

Xml代码  

  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方法,添加后其代码如下所示:

Java代码  

  1. public class ConsumerListener {
  2. public void receiveMessage(String message) {
  3. System.out.println("ConsumerListener通过receiveMessage接收到一个纯文本消息,消息内容是:" + message);
  4. }
  5. public void receiveMessage(Email email) {
  6. System.out.println("接收到一个包含Email的ObjectMessage。");
  7. System.out.println(email);
  8. }
  9. }

然后我们定义如下测试代码:

Java代码  

  1. @Test
  2. public void testObjectMessage() {
  3. Email email = new Email("[email protected]", "主题", "内容");
  4. producerService.sendMessage(adapterQueue, email);
  5. }

因为我们给MessageListenerAdapter指定了一个MessageConverter,而且是一个EmailMessageConverter,所以当MessageListenerAdapter接收到一个消息后,它会调用我们指定的MessageConverter的fromMessage方法把它转换成一个Java对象,根据定义这里会转换成一个Email对象,然后会把这个Email对象作为参数调用我们通过defaultListenerMethod属性指定的默认处理器方法,根据定义这里就是receiveMessage方法,但是我们可以看到在ConsumerListener中我们一共定义了两个receiveMessage方法,因为是通过转换后的Email对象作为参数进行方法调用的,所以这里调用的就应该是参数类型为Email的receiveMessage方法了。上述测试代码运行后会输出如下结果:

说到这里可能有读者就会有疑问了,说我们在之前讲解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为空就可以了。

Xml代码  

  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方法了。

Java代码  

  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。

Java代码  

  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. }
				
时间: 2024-10-18 10:08:57

Spring学习总结(11)——Spring JMS MessageConverter介绍的相关文章

Spring学习1:Spring基本特性

http://longliqiang88.github.io/2015/08/14/Spring%E5%AD%A6%E4%B9%A01%EF%BC%9ASpring%E5%9F%BA%E6%9C%AC%E7%89%B9%E6%80%A7/ Spring学习1:Spring基本特性 Spring基本特征 Spring基本特征Spring是一个非常活跃的开源框架:它是一个基于Core来构架多层JavaEE系统的框架,它的主要目地是简化企业开发.Spring以一种非侵入式的方式来管理你的代码,Spri

spring学习笔记(一) Spring概述

博主Spring学习笔记整理大部分内容来自Spring实战(第四版)这本书.  强烈建议新手购入或者需要电子书的留言. 在学习Spring之前,我们要了解这么几个问题:什么是Spring?Spring的优势在哪里?怎么系统的学习Spring? 一.什么是Spring? Spring是一个开源的轻量级Java SE(Java 标准版本)/Java EE(Java 企业版本)开发应用框架,其目的是用于简化企业级应用程序开发. 那有人就会问了,Spring是如何简化开发的? 在传统开发中,一个应用是需

spring学习 8-面试spring 问题

1.谈谈你对spring IOC和DI的理解,它们有什么区别? IoC Inverse of Control 反转控制的概念,就是将原本在程序中手动创建UserService对象的控制权,交由Spring框架管理,简单说,就是创建UserService对象控制权被反转到了Spring框架 DI:Dependency Injection 依赖注入,在Spring框架负责创建Bean对象时,动态的将依赖对象注入到Bean组件 面试题: IoC 和 DI的区别? IoC 控制反转,指将对象的创建权,反

Spring学习1-初识Spring

一.简介   1.Spring是一个开源的控制反转(Inversion of Control ,IoC)和面向切面(AOP)的容器框架.它的主要目得是简化企业开发.  2.为何要使用Spring?    i:降低组件之间的耦合度,实现软件各层之间的解耦.    ii:可以使用容器提供的众多服务,如:事务管理服务.消息服务等等.当我们使用容器管理事务时,开发人员就不再需要手工控制事务.也不需处理复杂的事务传播. i3:容器提供单例模式支持,开发人员不再需要自己编写实现代码. i4:容器提供了AOP

Spring学习(五)--构建Spring Web应用程序

一.Spring MVC起步 看过猫和老鼠的小伙伴都可以想象Tom猫所制作的捕鼠器:它的目标 是发送一个小钢球,让它经过一系列稀奇古怪的装置,最后触发捕鼠 器.小钢球穿过各种复杂的配件,从一个斜坡上滚下来,被跷跷板弹起,绕过一个微型摩天轮,然后被橡胶靴从桶中踢出去.经过这些后,小钢球会对那只可怜又无辜的老鼠进行捕获.而Spring MVC框架与捕鼠器有些类似.Spring将请求在调度Servlet.处理器映射(handler mapping).控制器以及视图解析器(view resolver)之

Spring学习笔记——关于Spring注解扫描不能注入new对象问题

这几天Leader让我用工厂模式对部分业务逻辑代码进行重构,过程是痛苦的(这里就不详细说了),结果很甜蜜.下面记录一下我在重构过程中遇到一个问题. 部分代码如下: @service(orderFactory) public class OrderFactory implements IOrderFactory{ public OrderCreate factory(String type){ if(type != null && type.indexOf("1")! =

Spring JMS——MessageConverter介绍

消息转换器MessageConverter MessageConverter的作用主要有两方面,一方面它可以把我们的非标准化Message对象转换成我们的目标Message对象,这主要是用在发送消息的时候:另一方面它又可以把我们的Message对象转换成对应的目标对象,这主要是用在接收消息的时候. 下面我们就拿发送一个对象消息来举例,假设我们有这样一个需求:我们平台有一个发送邮件的功能,进行发送的时候我们只是把我们的相关信息封装成一个JMS消息,然后利用JMS进行发送,在对应的消息监听器进行接收

Spring学习(11)---JSR-250标准注解之 @Resource、@PostConstruct、@PreDestroy

1)@Resource(JSR-250标准注解,推荐使用它来代替Spring专有的@Autowired注解) Spring 不但支持自己定义的@Autowired注解,还支持几个由JSR-250规范定义的注解,它们分别是@Resource.@PostConstruct以及@PreDestroy. @Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了.@Resource有两个属性是比较重要的,分别

spring学习一:spring入门及相关概念介绍

1:Spring的概念:(03年兴起) (1)   开源的轻量级的框架(无需复杂的环境,不依赖其他) (2)   一站式框架(Spring在javaee的三层结构中,对每一层都提供不同的解决技术: Web层:SpringMVC: service层:Spring的ioc: dao层:Spring的jdbcTemplate: Spring核心特征: AOP:面向切面编程,对业务的重复逻辑进行抽取,提高开发效率(如日志和权限的控制): IOC:控制反转,将类的管理交给spring的配置文件,通过DI(