1,消息监听容器
对于消息监听容器而言,除了要知道监听哪个目的地之外,还需要知道到哪里去监听,也就是说它还需要知道去监听哪个JMS服务器,这是通过在配置MessageConnectionFactory的时候往里面注入一个ConnectionFactory来实现的。所以我们在配置一个MessageListenerContainer的时候有三个属性必须指定,一个是表示从哪里监听的ConnectionFactory;一个是表示监听什么的Destination;一个是接收到消息以后进行消息处理的MessageListener。Spring一共为我们提供了两种类型的MessageListenerContainer,SimpleMessageListenerContainer和DefaultMessageListenerContainer。
SimpleMessageListenerContainer会在一开始的时候就创建一个会话session和消费者Consumer,并且会使用标准的JMS MessageConsumer.setMessageListener()方法注册监听器让JMS提供者调用监听器的回调函数。它不会动态的适应运行时需要和参与外部的事务管理。兼容性方面,它非常接近于独立的JMS规范,但一般不兼容Java EE的JMS限制。
大多数情况下我们还是使用的DefaultMessageListenerContainer,跟SimpleMessageListenerContainer相比,DefaultMessageListenerContainer会动态的适应运行时需要,并且能够参与外部的事务管理。它很好的平衡了对JMS提供者要求低、先进功能如事务参与和兼容Java EE环境。
2.消息监听器
在spring整合JMS的应用中我们在定义消息监听器的时候一共可以定义三种类型的消息监听器,分别是MessageListener、SessionAwareMessageListener和MessageListenerAdapter。下面就分别来介绍一下这几种类型的区别。
MessageListener是最原始的消息监听器,它是JMS规范中定义的一个接口。其中定义了一个用于处理接收到的消息的onMessage方法,该方法只接收一个Message参数。
1import javax.jms.JMSException; 2import javax.jms.Message; 3import javax.jms.MessageListener; 4import javax.jms.TextMessage; 5 6public class ConsumerMessageListener implements MessageListener { 7 8 public void onMessage(Message message) { 9 //这里我们知道生产者发送的就是一个纯文本消息,所以这里可以直接进行强制转换,或者直接把onMessage方法的参数改成Message的子类TextMessage 10 TextMessage textMsg = (TextMessage) message; 11 System.out.println("接收到一个纯文本消息。"); 12 try { 13 System.out.println("消息内容是:" + textMsg.getText()); 14 } catch (JMSException e) { 15 e.printStackTrace(); 16 } 17 } 18 19}
SessionAwareMessageListener是Spring为我们提供的,它不是标准的JMS MessageListener。MessageListener的设计只是纯粹用来接收消息的,假如我们在使用MessageListener处理接收到的消息时我们需要发送一个消息通知对方我们已经收到这个消息了,那么这个时候我们就需要在代码里面去重新获取一个Connection或Session。SessionAwareMessageListener的设计就是为了方便我们在接收到消息后发送一个回复的消息,它同样为我们提供了一个处理接收到的消息的onMessage方法,但是这个方法可以同时接收两个参数,一个是表示当前接收到的消息Message,另一个就是可以用来发送消息的Session对象。
1 public void onMessage(TextMessage message, Session session) throws JMSException { 2 System.out.println("收到一条消息"); 3 System.out.println("消息内容是:" + message.getText()); 4 MessageProducer producer = session.createProducer(destination); 5 Message textMessage = session.createTextMessage("ConsumerSessionAwareMessageListener。。。"); 6 producer.send(textMessage); 7 }
MessageListenerAdapter类实现了MessageListener接口和SessionAwareMessageListener接口,它的主要作用是将接收到的消息进行类型转换,然后通过反射的形式把它交给一个普通的Java类进行处理。 其另外一个主要的功能是可以自动的发送返回消息。
MessageListenerAdapter会把接收到的消息做如下转换:
TextMessage转换为String对象;
BytesMessage转换为byte数组;
MapMessage转换为Map对象;
ObjectMessage转换为对应的Serializable对象。
1<bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter"> 2 <property name="delegate"> 3 <bean class="com.tiantian.springintejms.listener.ConsumerListener"/> 4 </property> 5 <property name="defaultListenerMethod" value="receiveMessage"/> 6</bean> 7//或者 8 <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter"> 9 <constructor-arg> 10 <bean class="com.tiantian.springintejms.listener.ConsumerListener"/> 11 </constructor-arg> 12 <property name="defaultListenerMethod" value="receiveMessage"/> 13 </bean>
消息回复:
(1),可以通过发送的Message的setJMSReplyTo方法指定该消息对应的回复消息的目的地。 这里我们把我们的生产者发送消息的代码做一下修改,在发送消息之前先指定该消息对应的回复目的地为一个叫responseQueue的队列目的地,具体代码如下所示:
1 package com.tiantian.springintejms.service.impl; 2 3 import javax.jms.Destination; 4 import javax.jms.JMSException; 5 import javax.jms.Message; 6 import javax.jms.Session; 7 import javax.jms.TextMessage; 8 9 import org.springframework.beans.factory.annotation.Autowired; 10 import org.springframework.beans.factory.annotation.Qualifier; 11 import org.springframework.jms.core.JmsTemplate; 12 import org.springframework.jms.core.MessageCreator; 13 import org.springframework.stereotype.Component; 14 15 import com.tiantian.springintejms.service.ProducerService; 16 17 @Component 18 public class ProducerServiceImpl implements ProducerService { 19 20 @Autowired 21 private JmsTemplate jmsTemplate; 22 23 @Autowired 24 @Qualifier("responseQueue") 25 private Destination responseDestination; 26 27 public void sendMessage(Destination destination, final String message) { 28 System.out.println("---------------生产者发送消息-----------------"); 29 System.out.println("---------------生产者发了一个消息:" + message); 30 jmsTemplate.send(destination, new MessageCreator() { 31 public Message createMessage(Session session) throws JMSException { 32 TextMessage textMessage = session.createTextMessage(message); 33 textMessage.setJMSReplyTo(responseDestination); 34 return textMessage; 35 } 36 }); 37 } 38 39 }
xml配置文件:
1 <!-- 用于测试消息回复的 --> 2 <bean id="responseQueue" class="org.apache.activemq.command.ActiveMQQueue"> 3 <constructor-arg> 4 <value>responseQueue</value> 5 </constructor-arg> 6 </bean> 7 8 <!-- responseQueue对应的监听器 --> 9 <bean id="responseQueueListener" class="com.tiantian.springintejms.listener.ResponseQueueListener"/> 10 11 <!-- responseQueue对应的监听容器 --> 12 <bean id="responseQueueMessageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> 13 <property name="connectionFactory" ref="connectionFactory"/> 14 <property name="destination" ref="responseQueue"/> 15 <property name="messageListener" ref="responseQueueListener"/> 16 </bean>
ResponseQueueListener的定义如下所示:
1 public class ResponseQueueListener implements MessageListener { 2 3 public void onMessage(Message message) { 4 if (message instanceof TextMessage) { 5 TextMessage textMessage = (TextMessage) message; 6 try { 7 System.out.println("接收到发送到responseQueue的一个文本消息,内容是:" + textMessage.getText()); 8 } catch (JMSException e) { 9 e.printStackTrace(); 10 } 11 } 12 } 13 14 }
生产者发送消息被MessageListenerAdapter处理之后,MessageListenerAdapter把监听器的返回内容封装成一个Message,往原Message通过setJMSReplyTo方法指定的回复目的地发送了一个消息。对于MessageListenerAdapter对应的监听器处理方法返回的是一个null值或者返回类型是void的情况,MessageListenerAdapter是不会自动进行消息的回复的。
(2)通过MessageListenerAdapter的defaultResponseDestination属性来指定。在定义MessageListenerAdapter的时候通过其defaultResponseDestination属性指定其默认的回复目的地是“defaultResponseQueue”,并定义defaultResponseQueue对应的消息监听器和消息监听容器。
1<!-- 消息监听适配器 --> 2<bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter"> 3 <!-- <constructor-arg> 4 <bean class="com.tiantian.springintejms.listener.ConsumerListener"/> 5 </constructor-arg> --> 6 <property name="delegate"> 7 <bean class="com.tiantian.springintejms.listener.ConsumerListener"/> 8 </property> 9 <property name="defaultListenerMethod" value="receiveMessage"/> 10 <property name="defaultResponseDestination" ref="defaultResponseQueue"/> 11</bean> 12 13<!-- 消息监听适配器对应的监听容器 --> 14<bean id="messageListenerAdapterContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> 15 <property name="connectionFactory" ref="connectionFactory"/> 16 <property name="destination" ref="adapterQueue"/> 17 <property name="messageListener" ref="messageListenerAdapter"/><!-- 使用MessageListenerAdapter来作为消息监听器 --> 18</bean> 19 20!-- 默认的消息回复队列 --> 21<bean id="defaultResponseQueue" class="org.apache.activemq.command.ActiveMQQueue"> 22 <constructor-arg> 23 <value>defaultResponseQueue</value> 24 </constructor-arg> 25</bean> 26 27<!-- defaultResponseQueue对应的监听器 --> 28<bean id="defaultResponseQueueListener" class="com.tiantian.springintejms.listener.DefaultResponseQueueListener"/> 29 30<!-- defaultResponseQueue对应的监听容器 --> 31<bean id="defaultResponseQueueMessageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> 32 <property name="connectionFactory" ref="connectionFactory"/> 33 <property name="destination" ref="defaultResponseQueue"/> 34 <property name="messageListener" ref="defaultResponseQueueListener"/> 35</bean>
当两种方式都指定了消息的回复目的地的时候使用发送消息的setJMSReplyTo方法指定的目的地将具有较高的优先级,MessageListenerAdapter将只往该方法指定的消息回复目的地发送回复消息。