概述


JmsTemplate提供了3组*3,共计9个发送用的方法。
 
发送的方法有3组:
  1. 基本的发送
  2. 转换并发送
  3. 转换、后处理再发送

必需的资源


必需的资源有:
  • javax.jms.ConnectionFactory
    ConnectionFactory是客户端编程的开始,由它依次获取Connection、Session、Message、MessageProducer或MessageConsumer,从而做好了发送或接收的准备。
  • javax.jms.Destination
    Destination是发送的目的地,或者接收的源
实际上,我们提供了上述的资源之后,我们就是做了这样的指令:让JmsTemplate连接到Destination,从我们提供的ConnectionFactory中获取连接资源。
P.S.如果你不了客户端编程模型,建议参考02. JMS客户端编程模型

1.ConnectionFactory资源

我们如何提供ConnectionFactory给JmsTemplate?凭借JmsTemplete提供的构造器或setter方法:
  • public JmsTemplate(ConnectionFactory connectionFactory)
  • public void setConnectionFactory(ConnectionFactory connectionFactory)
    # 继承自org.springframework.jms.support.JmsAccessor

2.Destination资源

我们如何提供Destination给JmsTemplate?
我们有两个机会做这件事,第一次是在初始化的时候提供一个默认的Destination,第二次是在发送的时候提供一个明确的Destination。相关的方法:
  • public void setDefaultDestination(Destination destination)
    设置默认的Destination
  • public void send(Destination destination, MessageCreator messageCreator)
    将消息发送到指定的Destination
    MessageCreator接口用来提供创建消息的回调方法,后面再讲。
 
JmsTemplate还提供了另一种获取Destination的方式:基于Destination解析器、Destination类型、Destination名字的获取。你可以使用下面的代码来指定解析器、Destination类型:
 // jt is instance of JmsTemplate
jt.setDestinationResolver(new DynamicDestinationResolver()); // set Destination解析器
jt.setPubSubDomain(false); // set Destinantion类型
Destination解析器要实现org.springframework.jms.support.destination.DestinationResolver接口。解析器有默认的值,就是DynamicDestinationResolver,除非你要使用其他的解析器,否则不必调用setDestinationResolver。所以,第2行代码是多余的。
Destination类型有2个:false-Queue类型,true-Topic类型;默认为false。所以,第3行代码是多余的。
设置了解析器、类型,或者直接使用默认的值,之后,就可以设置destination的name。JmsTemplate提供了两个方法:
  • public void setDefaultDestinationName(String destinationName)
    设置defaultDestination。这个方法和setDefaultDestination(Destination destination)做同样的事情,只是这个方法依赖于解析器和类型。
  • public void send(String destinationName, MessageCreator messageCreator)
    将消息发送到指定的Destination。这个方法和send(Destination destination, MessageCreator messageCreator)做同样的事情,只是这个方法依赖于解析器和类型。
 
P.S.如何根据destinationName创建Destination实例?
javax.jms.Session提供了两个方法,分别创建两种类型的Destination:
  • Queue createQueue(java.lang.String queueName)
    根据name创建Queue类型的Destination
  • Topic createTopic(java.lang.String topicName)
    根据name创建Topic类型的Destination
我们看下DynamicDestinationResolver的源码:
 public class DynamicDestinationResolver implements DestinationResolver {
@Override
public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain)
throws JMSException {
Assert.notNull(session, "Session must not be null");
Assert.notNull(destinationName, "Destination name must not be null");
if (pubSubDomain) {
return resolveTopic(session, destinationName);
}
else {
return resolveQueue(session, destinationName);
}
}
protected Topic resolveTopic(Session session, String topicName) throws JMSException {
return session.createTopic(topicName);
}
protected Queue resolveQueue(Session session, String queueName) throws JMSException {
return session.createQueue(queueName);
}
}

根据这份源码,可以帮助理解pubSubDomain的机制,以及将JMS的api和Spring的Destination解析器这两个知识点连接起来。

1.基本的发送方法


在前文我们已经接触了两个,它们都是在发送的同时指定Destination。现把它们和第3种一起介绍:
  • public void send(Destination destination, MessageCreator messageCreator)
    将消息发送到指定的Destination
  • public void send(String destinationName, MessageCreator messageCreator)
    将消息发送到指定的Destination。这个方法和send(Destination destination, MessageCreator messageCreator)做同样的事情,只是这个方法依赖于解析器和类型。
  • public void send(MessageCreator messageCreator)
    将消息发送到defaultDestination。
    这个方法要求提前设置defaultDestination,你可以调用setDefaultDestination(Destination destination)或者setDefaultDestinationName(String destinationName)来满足这个前提。
在3个基本的发送方法中,都使用MessageCreator来创建消息。

使用MessageCreator创建消息

jms中的Message,是以接口javax.jms.Message为首的接口家族,这个家族的图谱是这样的:
javax.jms.Message
    |---- BytesMessage
    |---- MapMessage
    |---- ObjectMessage
    |---- StreamMessage
    |---- TextMessage
JMS将Message细分为5种类型,并在javax.jms.Session接口中分别定义了创建上述Message的多个方法,通常以create*Message为名,返回对应的Message类型。
 
在JMS的api中,只有javax.jms.Session能创建消息。
 
所以在Spring中,如果我们要创建Message,就要有Session。但是我们只有现成的ConnectionFactory,我们不应该走一遍从ConnectionFactory到Session的路,否则我们也不需要JmsTemplate帮我们发送了,因为剩下的工作也没多少了——关键是我们并没有从JMS的API中解脱出来。
所以有了MessageCreator的接口,它定义了一个回调的方法:
  • Message createMessage(Session session)
只要我们把MessageCreator的实例传给JmsTemplate,它就会在合适的时候调用这个方法,并发送返回的消息。
下面给一个例子:
 jt.send(DESTINATION_NAME, new MessageCreator() {

     public Message createMessage(Session session) throws JMSException {
String text = "A test for MessageCreator.";
Message message = session.createTextMessage(text);
return message;
}
});

2.转换并发送的方法


我们需要将数据装进JMS的Message,然后再发送。JMS的Message有5种具体的类型,不同的类型适合装载不同的数据。如果你不想做这段工作,而是希望能直接把数据丢给谁,然后由它来封装成Message——你只想准备数据,然后发送。那么接下来要介绍的方法,正适合你。
 
Spring为转换定义了一个接口:org.springframework.jms.support.converter.MessageConverter,这个接口定义了下面的两个方法:
  • Message toMessage(Object object, Session session)
    发送时用到
  • Object fromMessage(Message message)
    接收时用到
一般情况下,我们既不需要为MessageConverter提供实现,也不需要面向MessageConverter进行编程,所以我们实在没有必要关注上面的两个方法,扫一眼,有个大概的印象就够了。
 
说回JmsTemplate,它定义了下面的方法来设置Converter:
  • public void setMessageConverter(MessageConverter messageConverter)
而且在初始化的时候,会自动赋值一个SimpleMessageConverter类型的实例,所以我们甚至也不需要关心setMessageConverter方法了。
 
说了这不多,总结一下,如果要用到转换,我们需要多做什么工作?答案是不需要!
下面是具有转换功能的发送的方法,与基本的发送的方法进行对比:
转换发送 基本发送
  • public void convertAndSend(Destination destination, Object message)
    将message转换成JMS的Message,并发送到指定的Destination
  • public void convertAndSend(String destinationName, Object message)
    将message转换成JMS的Message,并发送到指定的Destination。
  • public void convertAndSend(Object message)
    将message转换成JMS的Message,并发送到defaultDestination。
  • public void send(Destination destination, MessageCreator messageCreator)
    将消息发送到指定的Destination
  • public void send(String destinationName, MessageCreator messageCreator)
    将消息发送到指定的Destination。
  • public void send(MessageCreator messageCreator)
    将消息发送到defaultDestination。
这两个系列的方法相似度很高,只是在创建消息的问题上有不同的处理:转换发送隐藏了消息的创建,基本发送需要实现MessageCreator接口来创建消息。
 
接下来是一个demo,我们将上面的demo也拿下来,做一个对比:
转换发送
 String message = "a message for test convertAndSend.";
jt.convertAndSend(DESTINATION_NAME, message);
基本发送
 jt.send(DESTINATION_NAME, new MessageCreator() {

     public Message createMessage(Session session) throws JMSException {
String text = "A test for MessageCreator.";
Message message = session.createTextMessage(text);
return message;
}
}); 

2比8,这纯粹是数学问题了。

3.转换、后处理再发送的方法


javax.jms.Message定义了很多的方法用来为消息添加头部信息或属性。但是如果我们要用转换并发送的方法,我们就接触不到Message类型的消息了,自然也无法为其添加任何信息。JmsTemplate提供了另一套发送的方法,允许我们使用自动转换,还允许我们能接触到转换后的消息,以便我们能做些什么。之后我们会返回处理后的Message,交给JmsTemplate发送。
 
Spring定义了org.springframework.jms.core.MessagePostProcessor接口来做后处理的事,它定义了一个唯一的方法:
  • Message postProcessMessage(Message message)
    对消息进行处理,并返回处理后的消息
 
让我们来看看这些方法,并与前文介绍的方法对比:
转换、后处理、发送 转换、发送
  • public void convertAndSend(Destination destination, Object message, MessagePostProcessor postProcessor)
  • public void convertAndSend(String destinationName, Object message, MessagePostProcessor postProcessor)
  • public void convertAndSend(Object message, MessagePostProcessor postProcessor)
  • public void convertAndSend(Destination destination, Object message)
    将message转换成JMS的Message,并发送到指定的Destination
  • public void convertAndSend(String destinationName, Object message)
    将message转换成JMS的Message,并发送到指定的Destination。
  • public void convertAndSend(Object message)
    将message转换成JMS的Message,并发送到defaultDestination。
再来看看两个demo的对比:
转换、后处理、发送
 String message = "a message for test convertProcessAndSend.";
jt.convertAndSend(DESTINATION_NAME, message,
new MessagePostProcessor() {
public Message postProcessMessage(Message message)
throws JMSException {
message.setIntProperty("order", 1);
return message;
}
}); 
转换、发送
 String message = "a message for test convertAndSend.";
jt.convertAndSend(DESTINATION_NAME, message); 

在转换、后处理、发送中,我们为Message设置了一个属性:order=1.

AMQ学习笔记 - 08. Spring-JmsTemplate之发送的更多相关文章

  1. Spring实战第五章学习笔记————构建Spring Web应用程序

    Spring实战第五章学习笔记----构建Spring Web应用程序 Spring MVC基于模型-视图-控制器(Model-View-Controller)模式实现,它能够构建像Spring框架那 ...

  2. 机器学习实战(Machine Learning in Action)学习笔记————08.使用FPgrowth算法来高效发现频繁项集

    机器学习实战(Machine Learning in Action)学习笔记————08.使用FPgrowth算法来高效发现频繁项集 关键字:FPgrowth.频繁项集.条件FP树.非监督学习作者:米 ...

  3. Spring实战第八章学习笔记————使用Spring Web Flow

    Spring实战第八章学习笔记----使用Spring Web Flow Spring Web Flow是一个Web框架,它适用于元素按规定流程运行的程序. 其实我们可以使用任何WEB框架写流程化的应 ...

  4. C++ GUI Qt4学习笔记08

    C++ GUI Qt4学习笔记08   qtc++signal图形引擎文档 本章介绍Qt的二维图形引擎,Qt的二维图形引擎是基于QPainter类的.<span style="colo ...

  5. Spring 源码学习笔记10——Spring AOP

    Spring 源码学习笔记10--Spring AOP 参考书籍<Spring技术内幕>Spring AOP的实现章节 书有点老,但是里面一些概念还是总结比较到位 源码基于Spring-a ...

  6. Spring 源码学习笔记11——Spring事务

    Spring 源码学习笔记11--Spring事务 Spring事务是基于Spring Aop的扩展 AOP的知识参见<Spring 源码学习笔记10--Spring AOP> 图片参考了 ...

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

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

  8. spring学习笔记(一) Spring概述

    博主Spring学习笔记整理大部分内容来自Spring实战(第四版)这本书.  强烈建议新手购入或者需要电子书的留言. 在学习Spring之前,我们要了解这么几个问题:什么是Spring?Spring ...

  9. Java架构师之路 Spring学习笔记(一) Spring介绍

    前言 这是一篇原创的Spring学习笔记.主要记录我学习Spring4.0的过程.本人有四年的Java Web开发经验,最近在面试中遇到面试官总会问一些简单但我不会的Java问题,让我觉得有必要重新审 ...

随机推荐

  1. ACM 竞赛高校联盟 练习赛 第二场 B&C

    B. 题解: 枚举约数即可,判断n个数能否填约数的整数倍 #include <iostream> #include <cstring> #include <cstdio& ...

  2. 【题解】洛谷P2418 yyy loves OI IV

    感觉很是妙啊……这题数次误入歧途...最开始想的二维dp,单调队列优化:无果,卒.于是没忍住看了下标签:暴力枚举?搜索?于是开始想记忆化搜索.以为会有什么很强的剪枝之类的:30分,卒.最后终于回到正道 ...

  3. [UOJ #52]【UR #4】元旦激光炮

    题目大意:交互题,给你三个有序数组,长度分别为$n\_a,n\_b,n\_c$,都不超过$10^5$.三个函数$get\_a(i),get\_b(i),get\_c(i)$,分别返回$a_i,b_i, ...

  4. BZOJ day1

    十题击破  1051108811921432195119682242245624632761

  5. 如何使用Eclipse调试framework

    1.下载Eclipse EE(下载地址:http://www.eclipse.org/downloads/) 2.下载并安装JDK(下载地址:http://www.oracle.com/technet ...

  6. Eclipse来push,fetch,rebase代码

    如何与项目里的其他人一起合作项目,提交代码并更新呢?这里提出我比最近用到的两种工具:一种是Eclipse,另外一个是SourceTree.个人推荐从事Java开发的话,可以用Eclipse.当然,还有 ...

  7. linux网络编程系列-TCP/IP模型

    ### OSI:open system interconnection ### 开放系统互联网模型是由ISO国际标准化组织定义的网络分层模型,共七层 1. 物理层:物理定义了所有电子及物理设备的规范, ...

  8. iBatis之Iterator的使用

    一:前言 现在这个项目使用的是iBatis,我刚刚开始的时候说是用MyBatis,因为我以前用过,觉得还是比较好用的啊,而且不像iBatis样,查什么一个字段不能多也不能少,觉得好无语啊. 二:内容 ...

  9. bzoj 3100 排列

    题目大意: 给你长度为 \(1e6\) 的序列, 求最大的 \(K\) 使得序列中含有一个 \(K\) 的排列 做法: 性质: 区间包含1, 元素不重, 区间最大值=区间长度 枚举一个 \(1\) 让 ...

  10. shell 灵活设置定时任务

    #!/bin/bash step=30 #间隔的秒数,不能大于60 for (( i = 0; i < 60; i=(i+step) )); do curl #调用链接 sleep $step ...