尽管消息接收可以使用消息监听器的方式替代模版方法,但是在发送的时候是无法替代的,在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. 括号配对问题_栈<stack>

    问题 A: 括号配对问题 时间限制: 3 Sec  内存限制: 128 MB提交: 3  解决: 2[提交][状态][讨论版] 题目描述 现在,有一行括号序列,请你检查这行括号是否配对. 输入 第一行 ...

  2. spfa(模板)

    spfa作为图论中的常用算法,深受各类出题人和各位OIer的喜爱: so,为了给大众创造福利,宝宝在此奉上spfa大发的思路和模板:以感谢社会, 感谢CCF,感谢CCTV, 感谢我的老师,感谢同学们, ...

  3. java调用cmd命令删除文件夹及其所有内容

    /** * *删除D盘下面test目录,感觉以前用io流遍历删除好慢! * **/ public static void main(String[] args) { Runtime run = Run ...

  4. Mysql分布式事务

    关于Mysql分布式事务介绍,可参考:http://blog.csdn.net/luckyjiuyi/article/details/46955337 分为两个阶段:准备和执行阶段.有两个角色:事务的 ...

  5. 继承下public,protected,private访问权限

    C++中派生类对基类成员的访问形式主要有以下两种: 1.内部访问:由派生类中新增成员对基类继承来的成员的访问. 2.对象访问:在派生类外部,通过派生类的对象对从基类继承来的成员的访问.今天给大家介绍在 ...

  6. 《Thinking in Java》十七章_容器深入研究_练习14(Page486)

    练习14 Properties的继承树如下:

  7. Sublime Text 无法使用Package Control的解决方法 以及 常用的插件安装过程

    大概一个月之前给 Macbook air 装 Sublime Text 3 的时候,遇到过这个问题,当时解决了,现在回想,感觉忘的七七八八了,赶紧趁着还没有全忘光的时候记下来,当时的过程记得不一定准确 ...

  8. DOM – (w3school)1.DOM 方法 + 2.DOM属性 + 3.DOM 元素

      1.DOM 方法   一些 DOM 对象方法 这里提供一些您将在本教程中学到的常用方法: 方法 描述 getElementById() 返回带有指定 ID 的元素. getElementsByTa ...

  9. HTTP 请求头中的 X-Forwarded-For

    https://imququ.com/post/x-forwarded-for-header-in-http.html

  10. .net转的时间戳用java去解析的代码

    /// <summary> /// 转换成java解析一致的时间戳 /// </summary> /// <param name="time"> ...