MOM系列文章之 - Spring Jms Integration 解读(一)

2014-11-24 07:36:59 · 作者: · 浏览: 0

前阵子对Spring Jms实现进行了一些扩展,借此机会系统化地研究了一下Spring对JMS的支持,整理成文,希望大家能够喜欢!

本文打算从两个维度(编程API和包结构)进行阐述,希望大家读完,能对Spring在JMS层面上做的事情有一个大致了解。当然喜欢扣细节的朋友,也欢迎提出你的疑惑!

第一部分:编程API

首先,让我们来看下Spring中我们最最经常用到的JmsTemplate,上图

\

从继承关系上,我们先来看下接口 JmsOperations,基本上可以归纳出这几类方法:

Conveniencemethods for sending messages

Conveniencemethods for sending auto-converted messages

Conveniencemethods for receiving messages

Conveniencemethods for receiving auto-converted messages

Conveniencemethods for browsing messages

但要注意的是这里面的方法throws出来的异常非JMS 1.1里面的标准JMSException,而是被转译过的JmsException。同时可以看出这个接口

充分遵循了CQRS原则。一个MQ其实就是Wrapper后的Queue,数据结构的知识告诉我们,queue有两种存储结构:Array and LinkedList。Array擅长随机读取,LinkedList则擅长删除更新操作,一旦底层采用 了LinkedList结构,Brower就是个大问题,这个要格外注意一下。

再来看下JmsDestinationAccessor,该类继承自JmsAccessor(该类实现了InitializingBean,不解释),注意里面的DestinationResolver类,主要是从简单的String类型的名字解析成具体的Destination,其默认的实现DynamicDestinationResolver基本上已经够用了。举个例子,倘若你要扩展将其解析成zookeeper可识别的Location,可以考虑实现该类。

好,终于轮到JmsTemplate了,先贴一段Javadoc(这里面有两个地方需要先了解下)

This template uses a org.springframework.jms.support.destination.DynamicDestinationResolver and a SimpleMessageConverter as default strategies for resolving a destination name or converting a message, respectively. These defaults can be overridden through the destinationResolver and messageConverter bean properties.

直白,不解释了。。。。。。

NOTE: The ConnectionFactory used with this template should return pooled Connections (or a single shared Connection) as well as pooled Sessions and MessageProducers. Otherwise, performance of ad-hoc JMS operations is going to suffer.

池化工厂,理由也很充分了。Spring只提供了SingleConnectionFactory,至于池化么,具体的Broker自己去实现,像AMQ在其内部就有基于Commons pool类库的PooledConnectionFactory。

ok,下面我们深入JmsTemplate,了解其中几个重要的方法:

/**
	 * Execute the action specified by the given action object within a
	 * JMS Session. Generalized version of {@code execute(SessionCallback)},
	 * allowing the JMS Connection to be started on the fly.
	 * 

Use {@code execute(SessionCallback)} for the general case. * Starting the JMS Connection is just necessary for receiving messages, * which is preferably achieved through the {@code receive} methods. * @param action callback object that exposes the Session * @param startConnection whether to start the Connection * @return the result object from working with the Session * @throws JmsException if there is any problem * @see #execute(SessionCallback) * @see #receive */ public T execute(SessionCallback action, boolean startConnection) throws JmsException { Assert.notNull(action, Callback object must not be null); Connection conToClose = null; Session sessionToClose = null; try { //通过事务同步管理器获取与当前线程绑定的Resouce,这里是JmsResourceHolder Session sessionToUse = ConnectionFactoryUtils.doGetTransactionalSession( getConnectionFactory(), this.transactionalResourceFactory, startConnection); if (sessionToUse == null) { conToClose = createConnection(); sessionToClose = createSession(conToClose); if (startConnection) { conToClose.start(); } sessionToUse = sessionToClose; } if (logger.isDebugEnabled()) { logger.debug(Executing callback on JMS Session: + sessionToUse); } return action.doInJms(sessionToUse); } catch (JMSException ex) { //注意这里的妙处 - 异常转译 throw convertJmsAccessException(ex); } finally { JmsUtils.closeSession(sessionToClose); Connecti