尽管消息接收可以使用消息监听器的方式替代模版方法,但是在发送的时候是无法替代的,在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. codeforces 500B.New Year Permutation 解题报告

    题目链接:http://codeforces.com/problemset/problem/500/B 题目意思:给出一个含有 n 个数的排列:p1, p2, ..., pn-1, pn.紧接着是一个 ...

  2. ios调用c#后台接口报文格式

    - NSString *soapMessage = - [NSString stringWithFormat: - @"<?xml version=\"1.0\" ...

  3. Hibernate创建hqll时报错

    Hibernate 问题,在执行Query session.createQuery(hql) 报错误 出错截图: 这条语句在java运行环境下,直接连数据库不出错,如果在hiberante,strut ...

  4. QtCreator 添加第三方头文件库文件路径

    打开工程名.pro文件 添加 INCLUDEPATH += $$PWD/../../Obelisk/thirdparty/prebuilt/include/LeapSDKOrion LIBS += - ...

  5. 存储过程使用CTE 和 case when

    未用SQL CTE and case when: ALTER PROCEDURE [dbo].[usp_rptDropboxBatchSummary1] )='ALL', )='ALL', )='AL ...

  6. c语言字符集

    一.字符常量 'A', 'B','\n','\'','1' 二.字符类型变量的赋值 char c1='A'; char c2='b'; char c3=65; c2='\''; c2='\n'; 三. ...

  7. limits.h头文件

    CHAR,SHRT,INT ,LLONG加_MAX后缀表示最大,加_MIN后缀表示最小,加U前缀表示无符号 UCHAR_MIN ,UCHAR_MAX sizeof()计算数所用的空间 #include ...

  8. Mysql 数据库无法删除 41 错误

    今天删除Mysql 数据库时候,没法删除,直接报错 41: 方法,进入 mysql的安装目录 我的是:D:\tools\Mysql\V76384-01\mysql-advanced-5.6.25-wi ...

  9. JS判断IE版本并在页面显示内容

    <script type="text/javascript"> var isIE = function (ver) { var b = document.createE ...

  10. 在Linux中安装JDK的步骤

    相信不少学习Java的朋友都在Windows操作系统中安装过JDK,这里就不对JDK做详细的介绍了. 在Windows下安装JDK可参考:JDK的安装和配置 1.下载JDK 我们可以去官网(http: ...