消息监听器

在Spring整合JMS的应用中我们在定义消息监听器的时候一共能够定义三种类型的消息监听器,各自是MessageListener、SessionAwareMessageListener和MessageListenerAdapter。

以下就分别来介绍一下这几种类型的差别。

MessageListener

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();   }   }   }  

SessionAwareMessageListener

SessionAwareMessageListener是Spring为我们提供的,它不是标准的JMS MessageListener。MessageListener的设计仅仅是纯粹用来接收消息的,假如我们在使用MessageListener处理接收到的消息时我们须要发送一个消息通知对方我们已经收到这个消息了,那么这个时候我们就须要在代码里面去又一次获取一个Connection或Session。SessionAwareMessageListener的设计就是为了方便我们在接收到消息后发送一个回复的消息。它相同为我们提供了一个处理接收到的消息的onMessage方法,可是这种方法能够同一时候接收两个參数,一个是表示当前接收到的消息Message。还有一个就是能够用来发送消息的Session对象。先来看一段代码:

package com.somnus.jms.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 void setDestination(Destination destination){this.destination = destination;}}

在上面代码中我们定义了一个SessionAwareMessageListener。在这个Listener中我们在接收到了一个消息之后。利用相应的Session创建了一个到destination的生产者和相应的消息,然后利用创建好的生产者发送相应的消息。

接着我们在Spring的配置文件里配置该消息监听器将处理来自一个叫sessionAwareQueue的目的地的消息,而且往该MessageListener中通过set方法注入其属性destination的值为queueDestination。

这样当我们的SessionAwareMessageListener接收到消息之后就会往queueDestination发送一个消息。

<?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.somnus" /> <!-- 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.somnus.jms.listener.ConsumerMessageListener"/> <!-- 能够获取session的MessageListener --> <bean id="consumerSessionAwareMessageListener" class="com.somnus.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");   }   }  

在上述測试代码中,我们通过前面定义好的生产者往我们定义好的SessionAwareMessageListener监听的sessionAwareQueue发送了一个消息。程序执行之后控制台输出例如以下:


         这说明我们已经成功的往sessionAwareQueue发送了一条纯文本消息。消息会被ConsumerSessionAwareMessageListener的onMessage方法进行处理,在onMessage方法中ConsumerSessionAwareMessageListener就是简单的把接收到的纯文本信息的内容打印出来了,之后再往queueDestination发送了一个纯文本消息,消息内容是“ConsumerSessionAwareMessageListener…”,该消息随后就被ConsumerMessageListener处理了。依据我们的定义,在ConsumerMessageListener中也仅仅是简单的打印了一下接收到的消息内容。

MessageListenerAdapter

MessageListenerAdapter类实现了MessageListener接口和SessionAwareMessageListener接口。它的主要作用是将接收到的消息进行类型转换,然后通过反射的形式把它交给一个普通的Java类进行处理。

MessageListenerAdapter会把接收到的消息做例如以下转换:

  1. TextMessage转换为String对象。
  2. BytesMessage转换为byte数组。
  3. MapMessage转换为Map对象;
  4. ObjectMessage转换为相应的Serializable对象。

既然前面说了MessageListenerAdapter会把接收到的消息做一个类型转换,然后利用反射把它交给真正的目标处理器——一个普通的Java类进行处理(假设真正的目标处理器是一个MessageListener或者是一个SessionAwareMessageListener,那么Spring将直接使用接收到的Message对象作为參数调用它们的onMessage方法。而不会再利用反射去进行调用),那么我们在定义一个MessageListenerAdapter的时候就须要为它指定这样一个目标类。这个目标类我们能够通过MessageListenerAdapter的构造方法參数指定,如:

  <!-- 消息监听适配器 -->  <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">  <constructor-arg>  <bean class="com.somnus.jms.listener.ConsumerListener"/>  </constructor-arg>  </bean>  

也能够通过它的delegate属性来指定。如:

  <!-- 消息监听适配器 -->  <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">  <property name="delegate"><!--我们能够加一个此类的子类,能够直接在子类中实现默认方法,从而避免使用这个delegate配置 --><bean class="com.somnus.jms.listener.ConsumerListener"/>  </property>  <property name="defaultListenerMethod" value="receiveMessage"/>  </bean>  

前面说了假设我们指定的这个目标处理器是一个MessageListener或者是一个SessionAwareMessageListener的时候Spring将直接利用接收到的Message对象作为方法參数调用它们的onMessage方法。可是假设指定的目标处理器是一个普通的Java类时Spring将利用Message进行了类型转换之后的对象作为參数通过反射去调用真正的目标处理器的处理方法,那么Spring是怎样知道该调用哪个方法呢?这是通过MessageListenerAdapter的defaultListenerMethod属性来决定的。当我们没有指定该属性时,Spring会默认调用目标处理器的handleMessage方法。

接下来我们来看一个演示样例,如果我们有一个普通的Java类ConsumerListener,其相应有两个方法,handleMessage和receiveMessage。其代码例如以下:

package com.somnus.jms.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);   }   }  

如果我们要把它作为一个消息监听器来监听发送到adapterQueue的消息,这个时候我们就能够定义一个相应的MessageListenerAdapter来把它当做一个MessageListener使用。

<!-- 消息监听适配器 -->
<bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">  <property name="delegate">  <bean class="com.somnus.jms.listener.ConsumerListener"/>  </property>  <property name="defaultListenerMethod" value="receiveMessage"/>
</bean>  

当然,有了MessageListener之后我们还须要配置其相应的MessageListenerContainer,这里配置例如以下:

<!-- 消息监听适配器相应的监听容器 -->
<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.somnus.jms.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.somnus.jms.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.somnus.jms.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.somnus.jms.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;   }   });   }   }  

接着定义一个叫responseQueue的队列目的地及其相应的消息监听器和监听容器。

<!-- 用于測试消息回复的 -->
<bean id="responseQueue" class="org.apache.activemq.command.ActiveMQQueue">  <constructor-arg>  <value>responseQueue</value>  </constructor-arg>
</bean>  <!-- responseQueue相应的监听器 -->
<bean id="responseQueueListener" class="com.somnus.jms.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>  

ResponseQueueListener的定义例如以下所看到的:

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();   }   }   }   }  

接着我们执行我们的測试代码,利用生产者往我们定义好的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.somnus.jms.listener.ConsumerListener"/>  </constructor-arg> -->  <property name="delegate">  <bean class="com.somnus.jms.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.somnus.jms.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>  

DefaultResponseQueueListener的代码例如以下所看到的:

package com.somnus.jms.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将仅仅往该方法指定的消息回复目的地发送回复消息。

转载于:https://www.cnblogs.com/ljbguanli/p/7298446.html

Spring整合JMS(二)——消息监听器相关推荐

  1. Spring整合JMS——基于ActiveMQ实现(一)

    Spring整合JMS--基于ActiveMQ实现(一) 1.1     JMS简介 JMS的全称是Java Message Service,即Java消息服务.它主要用于在生产者和消费者之间进行消息 ...

  2. Spring整合JMS(四)——事务管理

    2019独角兽企业重金招聘Python工程师标准>>> Spring整合JMS(四)--事务管理 博客分类: Spring Jms SpringJMS事务sessionTransac ...

  3. Spring整合ActiveMQ完成消息队列MQ编程

    <–start–> 第一步:新建一个maven,将工程命名为activeMQ_spring.在pom.xml文件中导入相关jar包. ①spring开发和测试相关的jar包: spring ...

  4. JMS--ActiveMq与spring整合(二)

    原文地址:http://blog.csdn.net/haoxingfeng/article/details/9167895 在我们和Spring 整合的时候,对于消费者而言我们有三种不同类型的监听器可 ...

  5. Spring整合JMS(三)——MessageConverter介绍

    1.4     消息转换器MessageConverter MessageConverter的作用主要有两方面,一方面它可以把我们的非标准化Message对象转换成我们的目标Message对象,这主要 ...

  6. Spring 整合 Mybatis - 二(切面、事务管理)

    紧接着上篇<Spring 整合 Mybatis - 一(基础)>,介绍Spring 整合 Mybatis的切面.事务管理. 1 增加切面aop功能 1.1 spring.xml sprin ...

  7. Spring整合ActiveMQ接收消息

    操作步骤 第一步:把Activemq相关的jar包,添加到工程中 第二步:创建一个MessageListener的实现类,负责监听 第三步:配置MessageListener监听器 第四步:初始化Sp ...

  8. java监控activemq,ActiveMQ与Spring整合-监听消息

    本课程全程使用目前比较流行的开发工具idea进行开发,涉及到目前互联网项目中常用的高并发解决方案技术, 如  dubbo,redis,solr,freemarker,activeMQ,springBo ...

  9. (转)RabbitMQ学习之spring整合发送异步消息(注解实现)

    http://blog.csdn.net/zhu_tianwei/article/details/40919249 实现使用Exchange类型为DirectExchange. routingkey的 ...

最新文章

  1. PC-lint 的代码实例
  2. html设置页面大小_如何将Word文档页面大小设置为16开?
  3. 开源项目PullToRefresh详解(一)——PullToRefreshListView
  4. 软件项目管理0813:法律合规
  5. 若依前后端分离版(vue)中配置页面跳转的路由
  6. Jenkins 设置镜像_Windows Docker Agent 镜像可以常规使用了
  7. javascript里你绝对用的上的字符分割函数--原创
  8. 从PHP5到PHP7自我封装MongoDB以及平滑升级
  9. banner特效php,jQuery自适应通栏宽屏banner幻灯片切换特效
  10. 后台服务器端技术点(前沿了解)
  11. (23)Vue.js组件介绍
  12. 2017.3.9 Harry and Christmas tree 失败总结
  13. 获取数据后插入到jsp页面(自用,没内容)
  14. arcobject c++实现检查要素是否为multipart(准确而且快 最主要是real 网上代码有问题)
  15. 程序员崩溃了,想拿的年终奖怎么说黄就黄?!
  16. LIS最长上升子序列详解+模板(dp和二分做法)
  17. TOGAF9-certification简介
  18. java职业教育考试题_云课堂智慧职教java职业证书题库答案
  19. 自主招生认可的英语竞赛有哪些
  20. Web排行榜相关排序算法总结

热门文章

  1. Spring Cloud: .yml属性文件配置 (完善中)
  2. 2022年湖南省高职单招(职业技能)(计算机信息技术)考试强化训练及答案
  3. 带宽储备能力超100 Tbps,华为云CDN保障平台从容应对流量高峰
  4. Unity实战 RTS3D即时战略游戏开发(三)
  5. POJ 2671 Jimmy's Bad Day ★ (区间DP)
  6. 谷歌优化有什么好处?外贸独立站如何提高谷歌优化排名?
  7. B站兄弟连Linux视频笔记
  8. 国防科大计算机专业分数线,国防科大录取分数线,在各省相当于什么大学?
  9. 思维导图之《一位股票投资家的良知:我为何放弃技术分析》
  10. October 2009