JMS为了Java开发人员与消息代理(message broker)交互和收发消息提供了一套标准API。而且,由于每个message broker都支持JMS,所以我们就不需要学习额外的消息API了。但是,由于JMS是如此的通用以至于使用它并不是十分方便。

<!--[if !supportLists]-->1.      <!--[endif]-->处理JMS样本代码

我们还记得一般的JDBC是如何笨拙地处理连接、语句、结果集和异常的。不幸地是,JMS处理方式和它类似,有很多样本代码。很多JMS例子中的代码都是重复的。因此,与JdbcTemplate类似,Spring提供了JmsTemplate来处理样本代码。

<!--[if !supportLists]-->2.      <!--[endif]-->使用JMS模板

你可以使用JmsTemplate来创建连接,获取会话并进行收发消息。这样就可以只关注于发送消息的开发或是处理接收消息。另外,JmsTemplate还可以细化JMSException的处理。JmsTemplate能够捕获该异常并不同的未检查(unchecked)子类异常,如:DestinationResolutionException、IllegalStateException等。

其实,JMSException提供了一组子类用于详尽地表明异常原因,但是他们都是checked类型的,所以也就必须被捕获,而JmsTemplate将会为你做这件事情。

绑定JmsTemplate

若要使用JmsTemplate,你需要在Spring配置文件中将其声明为一个bean,方法如下:

<bean id="jmsTemplate"

    class="org.springframework.jms.core.JmsTemplate">

    <property name="connectionFactory" ref="connectionFactory" />

</bean>

因此JmsTemplate需要知道如何连接到message broker,所以我们需要设置connectionFactory属性,令其引用一个JMS的ConnectionFactory接口实现类,在这里是connectionFactory。JmsTemplate还有一些其他属性,如果需要我们再介绍。

消息发送

对于RoadRantz例子来说,RoadRantz应用会给每个motorist发送一些有用的第三方信息,如果motorist对此感兴趣,他的信息就将被加到一个单独的销售系统中。当首次注册时,如果用户选择接收第三方信息,那么他们的名字和邮件地址就以JMS消息的方式被发送到这个系统中。

而在RoadRantz端,我们希望使用JmsTemplate来发送motorist信息给Roadantz销售系统。下面的RantzMarketingGateImpl即实现了与销售系统的交互。

public class RantzMarketingGatewayImpl

implements RantzMarketingGateway {

       public RantzMarketingGatewayImpl() {}

       public void sendMotoristInfo(final Motorist motorist) {

              jmsTemplate.send(destination, new MessageCreator() {

       public Message createMessage(Session s) throws JMSException

       {

              MapMessage msg = s.createMapMessage();

              msg.setString(“lastName”, motorist.getLastName());

              msg.setString(“firstName”, motorist.getFirstName());

              msg.setString(“email”, motorist.getEmail());

              return msg;

       }

});

}

private JmsTemplate jmsTemplate;

public void setJmsTemplate(JmsTemplate jmsTemplate) {

       this.jmsTemplate = jmsTemplate;

}

private Destination destination;

public void setDestination(Destination destination) {

       this.destination = destination;

}

}

上面代码中的sendMotoristInfo方法最为关键。其实它只是调用JmsTemplate的send方法来发送消息。

send方法的第一个参数是一个JMS Destination对象。这里使用了destination属性被注入到RantzMarketingGatewayImpl类。当调用send方法时,JmsTemplate将会获取一个JMS连接和会话并在发送端发送消息。代码中的MessageCreator用于创建消息,这里使用了一个匿名内部类。MessageCreator类的createMessage方法使用JMS的MapMessage来保存motorist的姓名和邮件地址。createMessage方法是一个回调方法,JmsTemplate使用它来创建用于被发送的消息。

JmsTemplate和Destination通过setter注入到RantzMarketingGateImpl。所以,在Spring中配置RantzMarketingGateImpl时,我们必须指定jmsTemplate和rantzDestination bean的引用:

<bean id="marketingGateway"

    class="org.roadrantz.marketing.RantzMarketingGatewayImpl">

    <property name="jmsTemplate" ref="jmsTemplate" />

    <property name="destination" ref="rantzDestination” />

</bean>

值得注意的是,sendMotorist方法只关注于消息的创建和发送——JmsTemplate为我们处理其他的代码,而且我们不需要手工去编写捕获JMSException的代码,JmsTemplate将会捕获被抛出的JMSException并将其以Spring的未检查异常重新抛出。

设置默认destination

前面我们已经指定了一个Destination用于将消息发送给它。这种方式非常方便,但是对于RantzMarketingGateImpl而言,我们总是发送motorist消息给相同的destination,所以这种方式就不那么方便了。除了每次发送消息时显式地指定destination,我们可以采用默认destination的方式:

<bean id="jmsTemplate"

    class="org.springframework.jms.core.JmsTemplate">

    <property name="connectionFactory" ref="connectionFactory" />

    <property name="defaultDestination" ref="rantzDestination" />

</bean>

现在调用send方法就不用再指定了destination参数了,只需要一个MessageCreator即可。JmsTemplate会将消息发送到默认的destination处,我们也不需要在RantzMarketingGateImpl的bean配置中指定destination了:

<bean id="marketingGateway"

    class="org.roadrantz.marketing.RantzMarketingGatewayImpl">

    <property name="jmsTemplate" ref="jmsTemplate" />

</bean>

因为我们不再需要在RantzMarketingGateImpl中指定destination,所以destination属性和代码中的setter方法都可以去掉了。

接收消息

JmsTemplate可以负责发送消息,它可以接收消息吗?是的,它可以。实际上用JmsTemplate接收消息更简单。你只需调用JmsTemplate的receive方法。

public class MarketingReceiverGatewayImpl {

       public MarketingReceiverGatewayImpl() {}

       public SpammedMotorist receiveSpammedMotorist() {

              MapMessage msg = (MapMessage)jmsTemplate.receive();

              SpammedMotorist motorist = new SpammedMotorist();

              try {

                     motorist.setFistName(msg.getString(“firstName”));

                     motorist.setLastName(msg.getString(“lastName”));

                     motorist.setEmail(msg.getString(“email”));

}

catch (JMSException e) {

       throw JmsUtils.convertJmsAccessException(e);

}

Return motorist;

}

private JmsTemplate jmsTemplate;

public void setJmsTemplate(JmsTemplate jmsTemplate) {

       this.jmsTemplate = jmsTemplate;

}

}

当调用receive方法时,JmsTemplate会再次帮我们处理那些诸如连接,会话之类的样本工作,然后再调用receive方法。JmsTemplate的receive方法是同步的。默认情况下,调用receive方法之后将会等待消息发送至destination。不过你可以指定一个接收超时receiveTimeout属性,例如:

<bean id="jmsTemplate"

    class="org.springframework.jms.core.JmsTemplate">

    <property name="connectionFactory" ref="connectionFactory" />

    <property name="defaultDestination" ref="rantzDestination" />

    <property name="receiveTimeout" value="60000" />

</bean>

当然,你可以使用指定的destination,如:

MapMessage msg = (MapMessage) jmsTemplate.receive(destination);

另外,你还可以通过名字来指定一个destination,并让Spring的destination解析器自动地解析destination,如:

MapMessage msg =

(MapMessage) jmsTemplate.receive(“rantz.marketing.queue”);

同步接收消息并不是唯一的选择。Spring也提供了消息的异步接收方式。下面让我们看看如何让JmsTemplate自动实现XML消息和Java对象之间的转换。

<!--[if !supportLists]-->3.      <!--[endif]-->消息转换

被发送的消息是由createMessage方法中的异步MessageCreator实例创建的,通过使用Motorist对象属性将其放入一个MapMessage对象。同时在接收端,MarketingReceiverGatewayImpl的receiveSpammedMotorist方法会从消息中获取值并赋值到一个SpammedMotorist对象中。本例中的转换工作并不复杂,但如果应用中的多个点多需要接收或发送相同的消息,那么就会出现很多重复代码。

Spring提供了专门用于消息转换的接口:MessageConverter。

public interface MessageConverter {

       public Message toMessage(Object object, Session session);

       public Object fromMessage(Message message);

}

MessageConverter接口很简单,只有两个方法。如果要发送消息,toMessage方法会将一个Java对象转换成消息。在接收端,fromMessage方法将消息转换成Java对象。我们需要在应用中实现该接口

收发转换过的消息

虽然我们可以使用toMessage和fromMessage方法来发送和接收消息,但Spring提供了更好的方法。除了显式地调用toMessage方法,我们可以调用JmsTemplate的convertAndSend方法。这样的话,RantzMarketingGateImpl的sendMotoristInfo方法就可以得到极大地简化:

public void sendMotoristInfo(final Motorist motorist) {

       jmsTemplate.convertAndSend(motorist);

}

JmsTemplate的convertAndSend方法会自动地调用toMessage方法用于发送消息。不适用默认destination的调用方式如下:

jmsTemplate.convertAndSend(destination, motorist);

jmsTemplate.convertAndSend(“rantz.marketing.queue”, motorist);

在接收端,我们也不必调用fromMessage方法,相反,我们可以使用receiveAndConvert方法:

public SpammedMotorist receiveSpammedMotorist() {

       return (SpammedMotorist) jmsTemplate.receiveAndConvert();

}

同样你也可是传递一个destination参数来使用非默认的destination。现在还有一个问题需要搞清楚,JmsTemplate的convertAndSend和receiveAndConvert方法使用消息转化器来进行消息转换,那么JmsTemplate是怎么知道这个消息转换器呢?

指定消息转换器

为了使之能够工作,我们需要在Spring中对其进行配置:

<bean id="motoristConverter"

    class="com.roadrantz.marketing.MotoristMessageConverter" />

然后,在JmsTemplate的配置中你需要将此converter指定到messageConverter属性上,如:

<bean id="jmsTemplate"

    class="org.springframework.jms.core.JmsTemplate">

    <property name="connectionFactory" ref="connectionFactory" />

    <property name="defaultDestination" ref="rantzDestination" />

    <property name="receiveTimeout" value="60000" />

    <property name="motoristConverter" ref="motoristConverter" />

</bean>

你可能已经发现了JdbcTemplate和JmsTemplate的许多相同之处,下面就让我们看看Spring是如何通过JmsGatewaySupport类来构建JMS通路的。

<!--[if !supportLists]-->4.      <!--[endif]-->JMS通路支持类

你可能还记得,因为有了JdbcDaoSupport这个用于编写基于JDBC的DAO的类,Spring可以更加方便地使用JdbcTemplate。同样地,Spring提供了JmsGatewaySupport类,它是提供JMS通路支持的基类。

目前为止我们创建了jmsTemplate和destination属性。尽管现在的代码量不多,但是如果应用中存在多个JMS通路,那必将导致大量重复的代码。为了解决这个问题,我们需要令RantzMarketingGateImpl类继承JmsGatewaySupport类,例如:

public class RantzMarketingGatewayImpl extends JmsGatewaySupport

       implements RantzMarketingGateway {

       public RantzMarketingGatewayImpl() {}

       public void sendMotoristInfo(final Motorist motorist) {

              getJmsTemplate().send(“rantz.marketing.queue”,

                     new MessageCreator() {

                     public Message createMessage(Session session)

throws JMSException {

MapMessage msg = session.createMapMessage();

msg.setString(“lastName”, motorist.getLastName());

msg.setString(“firstName”, motorist.getFirstName());

msg.setString(“email”, motorist.getEmail());

return msg;

                     }

              });

}

}

注意jmsTemplate属性和setJmsTemplate方法不见了。这个版本的RantzMarketingGateImpl会调用getJmsTemplate来获取JmsTemplate对象。所以,jmsTemplate和它的setter方法就不需要了。

不过JmsGatewaySupport是从哪里获得JmsTemplate对象的呢?你可以将一个JmsTemplate对象直接注入到jmsTemplate属性中,你也可以利用一个连接工厂属性:

<bean id="marketingGateway"

    class="com.roadrantz.marketing.RantzMarketingGatewayImpl" />

    <property name="connectionFactory" ref="connectionFactory" />

</bean>

这样配置的话,JmsGatewaySupport将自动根据指定的连接工厂来创建JmsTemplate对象。在Spring中不需额外声明JmsTemplate。不过这种方法也有不足之处:

·你只能为一个JmsTemplate指定一个默认destination。如果JmsGatewaySupport创建了自己的JmsTemplate,你就不能再指定默认destination了。你就只能显式地调用带有destination参数的send或receive方法。

·你只能为一个JmsTemplate编写一个消息转换器。如果JmsGatewaySupport创建了JmsTemplate,你就不能使用消息转换器了。你也必须显式地处理消息转换。

Spring中使用JMS的更多相关文章

  1. Spring JMSTemplate 与 JMS 原生API比较

    博客分类: JMS Spring 2.x   JMSUtil与Spring JmsTemplate的对比 Author:信仰 Date:2012-4-20 未完待续,截止日期2012-4-20 从以下 ...

  2. Spring框架之jms源码完全解析

    Spring框架之jms源码完全解析 我们在前两篇文章中介绍了Spring两大核心IOC(Inversion of Control控制反转)和AOP(Aspect Oriented Programmi ...

  3. Spring中文文档-第一部分

    一. Spring 框架概述 Spring是为了构建企业应用的轻量级框架.然而,Spring是模块化的,允许你只是使用其中的一部分,不需要引入其他的.你可以在任何web框架上使用IoC容器,也可以只使 ...

  4. Spring中文文档

    前一段时间翻译了Jetty的一部分文档,感觉对阅读英文没有大的提高(*^-^*),毕竟Jetty的受众面还是比较小的,而且翻译过程中发现Jetty的文档写的不是很好,所以呢翻译的兴趣慢慢就不大了,只能 ...

  5. Spring学习(三)——Spring中的依赖注入的方式

    [前面的话] Spring对我太重要了,做个关于web相关的项目都要使用Spring,每次去看Spring相关的知识,总是感觉一知半解,没有很好的系统去学习一下,现在抽点时间学习一下Spring.不知 ...

  6. Spring中的事务管理详解

    在这里主要介绍Spring对事务管理的一些理论知识,实战方面参考上一篇博文: http://www.cnblogs.com/longshiyVip/p/5061547.html 1. 事务简介: 事务 ...

  7. Spring消息之JMS.

    一.概念 异步消息简介 与远程调用机制以及REST接口类似,异步消息也是用于应用程序之间通信的. RMI.Hessian.Burlap.HTTP invoker和Web服务在应用程序之间的通信机制是同 ...

  8. 【sping揭秘】24、Spring框架对JMS的集成(无环境版,以后学MQ的时候再隆重介绍)& 任务调度和线程池

    这个我也不是很了解,那么这个需要好好学习一下了 JMS有2种消息域类型 1. point to point 点对点模式 2.发布订阅模式  publish/subscribe Pub/Sub 模式 传 ...

  9. 如何在优雅地Spring 中实现消息的发送和消费

    本文将对rocktmq-spring-boot的设计实现做一个简单的介绍,读者可以通过本文了解将RocketMQ Client端集成为spring-boot-starter框架的开发细节,然后通过一个 ...

随机推荐

  1. C# 加载配置文件

    //加载配置文件 var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .Add ...

  2. dokcer 的export 、improt和save 、load

    export .improt 是对容器操作也就是类似于虚拟机的快照 save .load 是针对于镜像操作的..

  3. python中多线程,多进程,多协程概念及编程上的应用

    1, 多线程 线程是进程的一个实体,是CPU进行调度的最小单位,他是比进程更小能独立运行的基本单位. 线程基本不拥有系统资源,只占用一点运行中的资源(如程序计数器,一组寄存器和栈),但是它可以与同属于 ...

  4. 浅析group by,having count()

    SELECT COUNT(*) FROM (SELECT COUNT(id),order_type,city_id,category_id,major_category_id,puid,user_id ...

  5. [真题] 一道 vsftp 运维题

    一道 vsftp 运维题 一.前言 在 V 站上凑巧看到了好友发的求助帖,五天时间一个理他的都没有.哈哈哈~ 废话不多说,我们来试试. 二.题目 这里我们假设存在这样的场景: 网络内有普通用户 ade ...

  6. C++多线程编程一

    1.C++多线程初步: #include <iostream> #include <thread> #include <Windows.h> using names ...

  7. UVA10140 Prime Distance

    UVA10140 Prime Distance 给定两个整数L,R(1<=L<=R<=2^{31},R-L<=10^6)L,R(1<=L<=R<=231,R− ...

  8. [redis]复制机制,调优,故障排查

    在redis的安装目录下首先启动一个redis服务,使用默认的配置文件,作为主服务 ubuntu@slave1:~/redis2$ ./redis-server ./redis.conf & ...

  9. 转一下大师兄的"MySQL高可用架构之MHA"

    简介: MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案,它由日本DeNA公司youshimaton(现就职于Facebook公司)开发,是 ...

  10. 2016级算法期末上机-H.难题·AlvinZH's Fight with DDLs III

    1119 AlvinZH's Fight with DDLs III 思路 难题,最小点覆盖. 分析题意,某一个任务,既可以在笔记本A的 \(a\) 模式下完成,也可以在笔记本B的 \(b\) 模式下 ...