尽管消息接收可以使用消息监听器的方式替代模版方法,但是在发送的时候是无法替代的,在Spring中必须要使用JmsTemplate提供的方法来进行发送操作,可见JmsTemplate类的重要性,那么我们对于Spring整合消息服务的分析就从JmsTemplate开始。

查看JmsTemplate的类型层级结构图发现实现了InitializingBean接口,接口方法实现是在JmsAccessor类中。发现函数中只是一个验证的功能,并没有逻辑实现。

public void afterPropertiesSet() {
if (getConnectionFactory() == null) {
throw new IllegalArgumentException("Property 'connectionFactory' is required");
}
}

在Spring中发送消息可以通过JmsTemplate中提供的方法来实现。

  public void send(final String destinationName, final MessageCreator messageCreator) throws JmsException {
execute(new SessionCallback<Object>() {
@Override
public Object doInJms(Session session) throws JMSException {
Destination destination = resolveDestinationName(session, destinationName);
doSend(session, destination, messageCreator);
return null;
}
}, false);
}

现在的风格不得不让我们回想起JdbcTemplate的类实现风格,极为相似,都是提取一个公共的方法作为最底层、最通用的功能实现,然后又通过回调函数的不同来区分个性化的功能。我们首先查看通用代码的抽取实现。

通用代码抽取execute()

在execute中封装了Connection以及Session的创建操作

  public <T> T execute(SessionCallback<T> action, boolean startConnection) throws JmsException {
Assert.notNull(action, "Callback object must not be null");
Connection conToClose = null;
Session sessionToClose = null;
try {
//事务相关
Session sessionToUse = ConnectionFactoryUtils.doGetTransactionalSession(
getConnectionFactory(), this.transactionalResourceFactory, startConnection);
if (sessionToUse == null) {
//创建connection
conToClose = createConnection();
//根据connection创建session
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 {
//关闭session
JmsUtils.closeSession(sessionToClose);
//释放连接
ConnectionFactoryUtils.releaseConnection(conToClose, getConnectionFactory(), startConnection);
}
}

为了发送一条消息需要做很多工作,需要很多的辅助代码,而这些代码又都是千篇一律的,没有任何的差异,所以execute方法的目的就是帮助我们抽离这些冗余代码使我们更加专注于业务逻辑的实现。从函数中看,这些冗余代码包括创建Connection、创建Session、当然也包括关闭Session和关闭Connection。而在准备工作结束后,调用回调函数将程序引入用户自定义实现的个性化处理。

发送消息的实现

有了基类辅助实现,使Spring更加专注于个性的处理,也就是说Spring使用execute方法中封装了冗余代码,而将个性化的代码实现放在了回调函数doInJms函数中。在发送消息的功能中回调函数通过局部类实现。

  protected void doSend(Session session, Destination destination, MessageCreator messageCreator)
throws JMSException {
Assert.notNull(messageCreator, "MessageCreator must not be null");
MessageProducer producer = createProducer(session, destination);
try {
Message message = messageCreator.createMessage(session);
if (logger.isDebugEnabled()) {
logger.debug("Sending created message: " + message);
}
doSend(producer, message);
// Check commit - avoid commit call within a JTA transaction.
if (session.getTransacted() && isSessionLocallyTransacted(session)) {
// Transacted session created by this template -> commit.
JmsUtils.commitIfNecessary(session);
}
}
finally {
JmsUtils.closeMessageProducer(producer);
}
}

在发送消息遵循着消息发送的规则,比如根据Destination创建MessageProducer、创建Message,并使用MessageProducer实例来发送消息。

接收消息

我们通常使用jmsTemplate.receive(destination)来接收简单的消息,那么这个功能Spring是如何封装的呢?

    @Override
public Message receive(Destination destination) throws JmsException {
return receiveSelected(destination, null);
}
@Override
public Message receiveSelected(final Destination destination, final String messageSelector) throws JmsException {
return execute(new SessionCallback<Message>() {
@Override
public Message doInJms(Session session) throws JMSException {
return doReceive(session, destination, messageSelector);
}
}, true);
}
protected Message doReceive(Session session, Destination destination, String messageSelector)
throws JMSException {
return doReceive(session, createConsumer(session, destination, messageSelector));
}
protected Message doReceive(Session session, MessageConsumer consumer) throws JMSException {
try {
// Use transaction timeout (if available).
long timeout = getReceiveTimeout();
JmsResourceHolder resourceHolder =
(JmsResourceHolder) TransactionSynchronizationManager.getResource(getConnectionFactory());
if (resourceHolder != null && resourceHolder.hasTimeout()) {
timeout = Math.min(timeout, resourceHolder.getTimeToLiveInMillis());
}
Message message = doReceive(consumer, timeout);
if (session.getTransacted()) {
// Commit necessary - but avoid commit call within a JTA transaction.
if (isSessionLocallyTransacted(session)) {
// Transacted session created by this template -> commit.
JmsUtils.commitIfNecessary(session);
}
}
else if (isClientAcknowledge(session)) {
// Manually acknowledge message, if any.
if (message != null) {
message.acknowledge();
}
}
return message;
}
finally {
JmsUtils.closeMessageConsumer(consumer);
}
}

实现的套路与发送差不多,同样还是使用execute函数来封装冗余的公共操作,而最终的目标还是通过consumer.receive()来接收消息,其中的过程就是对于MessageConsumer的创建以及一些辅助操作。

SpringJMS解析2-JmsTemplate的更多相关文章

  1. SpringJMS解析-JmsTemplate

    目录 通用代码抽取execute() 发送消息的实现 接收消息 尽管消息接收可以使用消息监听器的方式替代模版方法,但是在发送的时候是无法替代的,在Spring中必须要使用JmsTemplate提供的方 ...

  2. SpringJMS解析1-使用示例

    Spring配置文件: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="h ...

  3. SpringJMS解析--使用示例

    Spring配置文件: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="h ...

  4. SpringJMS解析3-监听器

    消息监听器容器是一个用于查看JMS目标等待消息到达的特殊bean,一旦消息到达它就可以获取到消息,并通过调用onMessage()方法将消息传递给一个MessageListener实现.Spring中 ...

  5. SpringJMS解析--监听器

    消息监听器容器是一个用于查看JMS目标等待消息到达的特殊bean,一旦消息到达它就可以获取到消息,并通过调用onMessage()方法将消息传递给一个MessageListener实现.Spring中 ...

  6. Spring JMS 官方文档学习

    最后部分的XML懒得写了,因为个人更倾向于JavaConfig形式. 为知笔记版本见这里,带格式~ 做了一个小demo,放到码云上了,有兴趣的点我. 说明:需要先了解下JMS的基础知识. 1.介绍 S ...

  7. spring-jms,spring-boot-starter-activemq JmsTemplate 发送方式

    spring-jms,spring-boot-starter-activemq JmsTemplate 发送方式 背景: 原来我准备是setDefaultDestinationName 设置队列的名称 ...

  8. AMQ学习笔记 - 13. Spring-jms的配置

    概述 如何使用spring-jms来简化jms客户端的开发? 这篇文章主要记录如何配置以便以后复用,而非原理的讲解,有些内容我 没有掌握原理. producer端 producer端负责发送,这里使用 ...

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

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

随机推荐

  1. Effective C++ -----条款51:编写new 和delete 时需固守常规

    operator new 应该内含一个无穷循环,并在其中尝试分配内存,如果它无法满足内存需求,就该调用new-handler.它也应该有能力处理0 bytes 申请.Class专属版本则还应该处理“比 ...

  2. Java多线程题库

    一.    填空题 处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入____阻塞_____状态. 处于新建状态的线程被启动 ...

  3. gcc提供的原子操作函数

    gcc从4.1.2提供了__sync_*系列的built-in函数,用于提供加减和逻辑运算的原子操作.其声明如下: type __sync_fetch_and_add (type *ptr, type ...

  4. Bootstrap学习(一)

    Bootstrap就是对jQuery的一次再开发,所以jQuery脚本引用必须在bootstrap脚本之前. 链接:http://www.cnblogs.com/vvjiang/p/5189804.h ...

  5. web storm

    常用插件: code glance 代码预览功能,用过的都知道有多爽...

  6. 两个viewport的故事(第一部分)

    原文:http://www.quirksmode.org/mobile/viewports.html 在这个迷你系列的文章里边我将会解释viewport,以及许多重要元素的宽度是如何工作的,比如< ...

  7. C#学习笔记---协变和逆变

    http://www.cnblogs.com/alphafly/p/4048608.html 协变是指方法能从委托的返回类型派生的一个类型. 逆变之方法获取的参数可以是委托参数类型的基类.

  8. EF – 4.CRUD与事务

    5.6.1 <Entity Framework数据更新概述>  首先介绍Entity Framework实现CRUD的基本方法,接着介绍了如何使用分部类增强和调整数据实体类的功能与行为特性 ...

  9. git branch用法总结

    git branch      git branch 不带参数:列出本地已经存在的分支,并且在当前分支的前面加“*”号标记,例如:   #git branch* master   newbranch ...

  10. 攻城狮在路上(叁)Linux(二十五)--- linux内存交换空间(swap)的构建

    swap的功能是应付物理内存不足的状况,用硬盘来暂时放置内存中的信息. 对于一般主机,物理内存都差不多够用,所以也就不会用到swap,但是对于服务器而言,当遇到大量网络请求时或许就会用到. 当swap ...