MessageListener是最原始的消息监听器,它是JMS规范中定义的一个接口。其中定义了一个用于处理接收到的消息的onMessage方法,该方法只接收一个Message参数。我们前面在讲配置消费者的时候用的消息监听器就是MessageListener,代码如下:
import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; public class ConsumerMessageListener implements MessageListener { public void onMessage(Message message) { //这里我们知道生产者发送的就是一个纯文本消息,所以这里可以直接进行强制转换,或者直接把onMessage方法的参数改成Message的子类TextMessage TextMessage textMsg = (TextMessage) message; System.out.println("接收到一个纯文本消息。"); try { System.out.println("消息内容是:" + textMsg.getText()); } catch (JMSException e) { e.printStackTrace(); } } }
package com.tiantian.springintejms.listener; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageProducer; import javax.jms.Session; import javax.jms.TextMessage; import org.springframework.jms.listener.SessionAwareMessageListener; public class ConsumerSessionAwareMessageListener implements SessionAwareMessageListener<TextMessage> { private Destination destination; public void onMessage(TextMessage message, Session session) throws JMSException { System.out.println("收到一条消息"); System.out.println("消息内容是:" + message.getText()); MessageProducer producer = session.createProducer(destination); Message textMessage = session.createTextMessage("ConsumerSessionAwareMessageListener。。。"); producer.send(textMessage); } public Destination getDestination() { returndestination; } public void setDestination(Destination destination) { this.destination = destination; } }
<?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/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd"> <context:component-scan base-package="com.tiantian" /> <!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 --> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 --> <property name="connectionFactory" ref="connectionFactory"/> </bean> <!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供--> <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61616"/> </bean> <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory --> <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory"> <!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory --> <property name="targetConnectionFactory" ref="targetConnectionFactory"/> </bean> <!--这个是队列目的地--> <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg> <value>queue</value> </constructor-arg> </bean> <!--这个是sessionAwareQueue目的地--> <bean id="sessionAwareQueue" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg> <value>sessionAwareQueue</value> </constructor-arg> </bean> <!-- 消息监听器 --> <bean id="consumerMessageListener" class="com.tiantian.springintejms.listener.ConsumerMessageListener"/> <!-- 可以获取session的MessageListener --> <bean id="consumerSessionAwareMessageListener" class="com.tiantian.springintejms.listener.ConsumerSessionAwareMessageListener"> <property name="destination" ref="queueDestination"/> </bean> <!-- 消息监听容器 --> <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="connectionFactory" /> <property name="destination" ref="queueDestination" /> <property name="messageListener" ref="consumerMessageListener" /> </bean> <bean id="sessionAwareListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="connectionFactory" /> <property name="destination" ref="sessionAwareQueue" /> <property name="messageListener" ref="consumerSessionAwareMessageListener" /> </bean> </beans>
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("/applicationContext.xml") public class ProducerConsumerTest { @Autowired private ProducerService producerService; @Autowired @Qualifier("sessionAwareQueue") private Destination sessionAwareQueue; @Test public void testSessionAwareMessageListener() { producerService.sendMessage(sessionAwareQueue, "测试SessionAwareMessageListener"); } }
这说明我们已经成功的往sessionAwareQueue发送了一条纯文本消息,消息会被ConsumerSessionAwareMessageListener的onMessage方法进行处理,在onMessage方法中ConsumerSessionAwareMessageListener就是简单的把接收到的纯文本信息的内容打印出来了,之后再往queueDestination发送了一个纯文本消息,消息内容是“ConsumerSessionAwareMessageListener…”,该消息随后就被ConsumerMessageListener处理了,根据我们的定义,在ConsumerMessageListener中也只是简单的打印了一下接收到的消息内容。
<!-- 消息监听适配器 --> <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter"> <constructor-arg> <bean class="com.tiantian.springintejms.listener.ConsumerListener"/> </constructor-arg> </bean>
<!-- 消息监听适配器 --> <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter"> <property name="delegate"> <bean class="com.tiantian.springintejms.listener.ConsumerListener"/> </property> <property name="defaultListenerMethod" value="receiveMessage"/> </bean>
package com.tiantian.springintejms.listener; public class ConsumerListener { public void handleMessage(String message) { System.out.println("ConsumerListener通过handleMessage接收到一个纯文本消息,消息内容是:" + message); } public void receiveMessage(String message) { System.out.println("ConsumerListener通过receiveMessage接收到一个纯文本消息,消息内容是:" + message); } }
<!-- 消息监听适配器 --> <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter"> <property name="delegate"> <bean class="com.tiantian.springintejms.listener.ConsumerListener"/> </property> <property name="defaultListenerMethod" value="receiveMessage"/> </bean>
<!-- 消息监听适配器对应的监听容器 --> <bean id="messageListenerAdapterContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="connectionFactory"/> <property name="destination" ref="adapterQueue"/> <property name="messageListener" ref="messageListenerAdapter"/><!-- 使用MessageListenerAdapter来作为消息监听器 --> </bean>
在上面的MessageListenerAdapter中我们指定了其defaultListenerMethod属性的值为receiveMessage,所以当MessageListenerAdapter接收到消息之后会自动的调用我们指定的ConsumerListener的receiveMessage方法。
针对于上述代码我们定义测试代码如下:
package com.tiantian.springintejms.test; import javax.jms.Destination; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.tiantian.springintejms.service.ProducerService; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("/applicationContext.xml") public class ProducerConsumerTest { @Autowired @Qualifier("adapterQueue") private Destination adapterQueue; @Test public void testMessageListenerAdapter() { producerService.sendMessage(adapterQueue, "测试MessageListenerAdapter"); } }
如果我们不指定MessageListenerAdapter的defaultListenerMethod属性,那么在运行上述代码时控制台会输出如下结果:
MessageListenerAdapter除了会自动的把一个普通Java类当做MessageListener来处理接收到的消息之外,其另外一个主要的功能是可以自动的发送返回消息。
当我们用于处理接收到的消息的方法的返回值不为空的时候,Spring会自动将它封装为一个JMS Message,然后自动进行回复。那么这个时候这个回复消息将发送到哪里呢?这主要有两种方式可以指定。
第一,可以通过发送的Message的setJMSReplyTo方法指定该消息对应的回复消息的目的地。这里我们把我们的生产者发送消息的代码做一下修改,在发送消息之前先指定该消息对应的回复目的地为一个叫responseQueue的队列目的地,具体代码如下所示:
package com.tiantian.springintejms.service.impl; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.Session; import javax.jms.TextMessage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.core.MessageCreator; import org.springframework.stereotype.Component; import com.tiantian.springintejms.service.ProducerService; @Component public class ProducerServiceImpl implements ProducerService { @Autowired private JmsTemplate jmsTemplate; @Autowired @Qualifier("responseQueue") private Destination responseDestination; public void sendMessage(Destination destination, final String message) { System.out.println("---------------生产者发送消息-----------------"); System.out.println("---------------生产者发了一个消息:" + message); jmsTemplate.send(destination, new MessageCreator() { public Message createMessage(Session session) throws JMSException { TextMessage textMessage = session.createTextMessage(message); textMessage.setJMSReplyTo(responseDestination); return textMessage; } }); } }
<!-- 用于测试消息回复的 --> <bean id="responseQueue" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg> <value>responseQueue</value> </constructor-arg> </bean> <!-- responseQueue对应的监听器 --> <bean id="responseQueueListener" class="com.tiantian.springintejms.listener.ResponseQueueListener"/> <!-- responseQueue对应的监听容器 --> <bean id="responseQueueMessageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="connectionFactory"/> <property name="destination" ref="responseQueue"/> <property name="messageListener" ref="responseQueueListener"/> </bean>
public class ResponseQueueListener implements MessageListener { public void onMessage(Message message) { if (message instanceof TextMessage) { TextMessage textMessage = (TextMessage) message; try { System.out.println("接收到发送到responseQueue的一个文本消息,内容是:" + textMessage.getText()); } catch (JMSException e) { e.printStackTrace(); } } } }
/** * 当返回类型是非null时MessageListenerAdapter会自动把返回值封装成一个Message,然后进行回复 * @param message * @return */ public String receiveMessage(String message) { System.out.println("ConsumerListener通过receiveMessage接收到一个纯文本消息,消息内容是:" + message); return "这是ConsumerListener对象的receiveMessage方法的返回值。"; }
我们可以看到在上述负责接收消息的receiveMessage方法有一个非空的返回值。
接着我们运行我们的测试代码,利用生产者往我们定义好的MessageListenerAdapter负责处理的adapterQueue目的地发送一个消息。测试代码如下所示:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("/applicationContext.xml") public class ProducerConsumerTest { @Autowired private ProducerService producerService; @Qualifier("adapterQueue") @Autowired private Destination adapterQueue; @Test public void testMessageListenerAdapter() { producerService.sendMessage(adapterQueue, "测试MessageListenerAdapter"); } }
这说明我们的生产者发送消息被MessageListenerAdapter处理之后,MessageListenerAdapter确实把监听器的返回内容封装成一个Message往原Message通过setJMSReplyTo方法指定的回复目的地发送了一个消息。对于MessageListenerAdapter对应的监听器处理方法返回的是一个null值或者返回类型是void的情况,MessageListenerAdapter是不会自动进行消息的回复的,有兴趣的网友可以自己测试一下。
第二,通过MessageListenerAdapter的defaultResponseDestination属性来指定。这里我们也来做一个测试,首先维持生产者发送消息的代码不变,即发送消息前不通过Message的setJMSReplyTo方法指定消息的回复目的地;接着我们在定义MessageListenerAdapter的时候通过其defaultResponseDestination属性指定其默认的回复目的地是“defaultResponseQueue”,并定义defaultResponseQueue对应的消息监听器和消息监听容器。
<!-- 消息监听适配器 --> <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter"> <!-- <constructor-arg> <bean class="com.tiantian.springintejms.listener.ConsumerListener"/> </constructor-arg> --> <property name="delegate"> <bean class="com.tiantian.springintejms.listener.ConsumerListener"/> </property> <property name="defaultListenerMethod" value="receiveMessage"/> <property name="defaultResponseDestination" ref="defaultResponseQueue"/> </bean> <!-- 消息监听适配器对应的监听容器 --> <bean id="messageListenerAdapterContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="connectionFactory"/> <property name="destination" ref="adapterQueue"/> <property name="messageListener" ref="messageListenerAdapter"/><!-- 使用MessageListenerAdapter来作为消息监听器 --> </bean> <!-- 默认的消息回复队列 --> <bean id="defaultResponseQueue" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg> <value>defaultResponseQueue</value> </constructor-arg> </bean> <!-- defaultResponseQueue对应的监听器 --> <bean id="defaultResponseQueueListener" class="com.tiantian.springintejms.listener.DefaultResponseQueueListener"/> <!-- defaultResponseQueue对应的监听容器 --> <bean id="defaultResponseQueueMessageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="connectionFactory"/> <property name="destination" ref="defaultResponseQueue"/> <property name="messageListener" ref="defaultResponseQueueListener"/> </bean>
package com.tiantian.springintejms.listener; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; public class DefaultResponseQueueListener implements MessageListener { public void onMessage(Message message) { if (message instanceof TextMessage) { TextMessage textMessage = (TextMessage) message; try { System.out.println("DefaultResponseQueueListener接收到发送到defaultResponseQueue的一个文本消息,内容是:" + textMessage.getText()); } catch (JMSException e) { e.printStackTrace(); } } } }这时候运行如下测试代码:
@Test public void testMessageListenerAdapter() { producerService.sendMessage(adapterQueue, "测试MessageListenerAdapter"); }控制台将输出如下内容:
这说明MessageListenerAdapter会自动把真正的消息处理器返回的非空内容封装成一个Message发送回复消息到通过defaultResponseDestination属性指定的默认消息回复目的地。
既然我们可以通过两种方式来指定MessageListenerAdapter自动发送回复消息的目的地,那么当我们两种方式都指定了而且它们的目的地还不一样的时候会怎么发送呢?是两个都发还是只发其中的一个呢?关于这部分的测试我这里就不赘述了,有兴趣的网友可以自己进行。这里我可以直接的告诉大家,当两种方式都指定了消息的回复目的地的时候使用发送消息的setJMSReplyTo方法指定的目的地将具有较高的优先级,MessageListenerAdapter将只往该方法指定的消息回复目的地发送回复消息。