《Spring 5 官方文档》26. JMS(一)

简介:

26.1 介绍

Spring 提供了一个 JMS 的集成框架,简化了 JMS API 的使用,就像 Spring 对 JDBC API 的集成一样。

JMS 大致可分为两块功能,即消息的生产与消费。JmsTemplate类用于消息生产和消息的同步接收。 对于类似 Java EE 的消息驱动 Bean 形式的异步接收,Spring 提供了大量用于创建消息驱动 POJOs(MDPs)的消息监听器。Spring 还提供了一种创建消息侦听器的声明式方法。

org.springframework.jms.core包提供了使用 JMS 的核心功能。它包含了 JMS 模板类,用来处理资源的创建与释放,从而简化 JMS 的使用,就像JdbcTemplate对 JDBC 做的一样。像其他大多数 Spring 模板类一样,JMS 模板类提供了执行公共操作的 helper 方法。在需要更复杂应用的情况下,类把处理任务的核心委托给用户实现的回调接口。JMS 类提供了方便的方法,用来发送消息、同步地使用消息以及向用户公开 JMS 会话和消息的生产者。

org.springframework.jms.support包提供了转换 JMSException 的功能。
转换代码将受检查的JMSException层转换到不受检查异常的镜像层。如果有一个提供者指定的受检查的javax.jms.JMSException类的子类,这个子类异常被封装到了不受检查的UncategorizedJmsException异常中。

org.springframework.jms.support.converter包提供了 MessageConverter 抽象,进行 Java 对象和 JMS 消息的互相转换。

org.springframework.jms.support.destination包提供了管理 JMS 目的地的不同策略,比如针对 JNDI 中保存的目标的服务定位器。

org.springframework.jms.annotation包提供了支持注解驱动监听端点的必要基础架构,通过使用@JmsListener实现。

org.springframework.jms.config包提供了 JMS 命名空间的解析实现,以及配置监听容器和创建监听端点的 java 配置支持。

最后,org.springframework.jms.connection包提供了适用于独立应用程序的ConnectionFactory实现。 它还包含 Spring 对 JMS 的PlatformTransactionManager实现(即JmsTransactionManager)。这将允许 JMS 作为事务性资源无缝集成到 Spring 的事务管理机制中。

26.2 Spring JMS的使用

26.2.1 JmsTemplate

JmsTemplate类是JMS核心包中的中心类。它简化了 JMS 的使用,因为在发送或同步接收消息时它帮我们处理了资源的创建和释放。

使用JmsTemplate的代码只需要实现规范中定义的回调接口。在JmsTemplate中通过调用代码让MessageCreator回调接口用所提供的会话(Session)创建消息。然而,为了顾及更复杂的 JMS API 应用,回调接口SessionCallback将 JMS 会话提供给用户,回调接口ProducerCallback则公开了SessionMessageProducer的组合。

JMS API 公开了发送方法的两种类型,一种接受交付模式、优先级和存活时间作为服务质量(QOS)参数,另一种则使用缺省值作为 QOS 参数(无需参数)方式。由于 JmsTemplate 中有很多发送方法,QOS 参数用 bean 属性进行暴露设置,从而避免在一系列发送方法中的重复。同样地,使用setReceiveTimeout属性设置用于同步接收调用的超时值。

一些 JMS 提供者通过配置ConnectionFactory,管理方式上允许默认的 QOS值 的设置。MessageProducer的发送方法send(Destination destination, Message message)在那些专有的 JMS 中将会使用不一样的 QOS 默认值。 所以,为了提供对 QOS 值域、的统一管理,JmsTemplate必须通过设置布尔值属性isExplicitQosEnabled为true,使它能够使用自己的QOS值。

为了方便起见,JmsTemplate还暴露了一个基本的请求-回复操作,允许在一个作为操作一部分而被创建的临时队列上,进行消息的发送与等待回复。

配置的JmsTemplate类的实例是线程安全的。这很重要,因为这意味着你可以配置一个JmsTemplate单例,然后安全地将这个共享引用注入给多个协作者。 要清楚,保持对ConnectionFactory引用的JmsTemplate是有状态的,但该状态不是会话状态。

从 Spring Framework 4.1开始,JmsMessagingTemplate构建在JmsTemplate之上,并提供与消息抽象层(即org.springframework.messaging.Message)的集成。 这允许你以通用的方式来创建要发送的消息。

26.2.2 Connections

JmsTemplate需要一个对ConnectionFactory的引用。 ConnectionFactory是 JMS 规范的一部分,并被作为使用 JMS 的入口。客户端应用通常作为一个工厂配合 JMS 提供者去创建连接,并封装一系列的配置参数,其中一些是和供应商相关的,例如 SSL 的配置选项。

当在 EJB 内使用 JMS 时,供应商提供 JMS 接口的实现,以至于可以参与声明式事务的管理,提供连接池和会话池。为了使用这个实现,J2EE 容器一般要求你在 EJB 或 servlet 部署描述符中将 JMS 连接工厂声明为 resource-ref。为确保可以在 EJB 内使用JmsTemplate的这些特性,客户端应当确保它能引用其中的ConnectionFactory实现。

缓存消息资源

标准的API涉及创建许多中间对象。要发送消息,将执行以下“API”步骤

ConnectionFactory->Connection->Session->MessageProducer->send

ConnectionFactorySend操作之间,有三个中间对象被创建和销毁。 为了优化资源使用并提高性能,提供了两个ConnectionFactory的实现。

SingleConnectionFactory

Spring 提供ConnectionFactory接口的一个实现,SingleConnectionFactory,它将在所有的createConnection调用中返回同一个的连接,并忽略close的调用。这在测试和独立的环境中相当有用,因为同一个连接可以被用于多个JmsTemplate调用以跨越多个事务。 SingleConnectionFactory接受一个通常来自 JNDI 的标准ConnectionFactory的引用。

CachingConnectionFactory

CachingConnectionFactory扩展了SingleConnectionFactory的功能,它添加了会话、消息生产者、消息消费者的缓存。 初始缓存大小设置为1,使用sessionCacheSize属性来增加缓存会话的数量。请注意,实际缓存会话的数量将超过该值,因为会话的缓存是基于确认模式的,因此当设置sessionCacheSize为1时,缓存的会话可能达到4个,因为每个确认模式都会缓存一个。当缓存的时候,消息生产者和消息消费者被缓存在他们自己的会话中同时也考虑到生产者和消费者的唯一属性。消息生产者基于他们的目的地被缓存,消息消费者基于目的地、选择器、非本地传送标识和持久订阅名称(假设创建持久消费者)的组合键被缓存。

26.2.3 Destination 管理

目的地(Destination),像ConnectionFactories一样,是可以在 JNDI 中进行存储和提取的 JMS 管理对象。当配置一个 Spring 应用上下文,可以使用 JNDI 工厂类JndiObjectFactoryBean / <jee:jndi-lookup>将你的对象引用依赖注入到 JMS 目的地。然而,如果在应用中有大量的目的地,或者 JMS 供应商提供了特有的高级目的地管理特性,这个策略常常显得很笨重。高级目的地管理的例子如创建动态目的地或支持目的地的命名层次。JmsTemplate将目的地名称到 JMS 目的地对象的解析委派给一个DestinationResolver接口的实现。DynamicDestinationResolverJmsTemplate使用的默认实现,并且提供动态目的地解析。同时JndiDestinationResolver作为 JNDI 包含的目的地的服务定位器,并且可选择地退回来使用DynamicDestinationResolver提供的行为。

相当常见的是在一个 JMS 应用中所使用的目的地只有在运行时才知道,因此,当一个应用被部署时,它不能被创建。这经常是因为交互系统组件之间的共享应用逻辑是在运行时按照已知的命名规范创建目的地。虽然动态目的地的创建不是 JMS 规范的一部分,但是许多供应商已经提供了这个功能。用户为所建的动态目的地定义名称,这样区别于临时的目的地,并且动态目的地不会被注册到 JNDI 中。创建动态目的地所使用的 API 在不同的供应商之间差别很大,因为目的地所关联的属性是供应商特有的。然而,有时由供应商作出的一个简单的实现选择是忽略 JMS 规范中的警告,并使用TopicSession的方法createTopic(String topicName)或者QueueSession的方法createQueue(String queueName)来创建一个拥有默认属性的新目的地。依赖于供应商的实现,DynamicDestinationResolver也可能创建一个物理上的目的地,而不是只是解析。

布尔属性PubSubDomain被用来配置JmsTemplate使用什么样的 JMS 域。这个属性的默认值是 false,使用点到点的队列。JmsTemplate使用该属性决定了通过DestinationResolver的实现来解析动态目的地的行为。

你还可以通过属性DefaultDestination配置一个带有默认目的地的JmsTemplate。默认的目的地被使用时,它的发送和接收操作不需要指定一个特定的目的地。

26.2.4 消息监听容器

在 EJB 世界里,JMS 消息最常用的功能之一是用于实现消息驱动 Bean(MDB)。Spring 提供了一个方法来创建消息驱动的 POJO(MDP),并且不会把用户绑定在某个 EJB 容器上。(参见第26.4.2节“异步接收 – 消息驱动的 POJO”,详细介绍了 Spring 的 MDP 支持)。从 Spring Framework 4.1开始,端点方法可以简单使用 @JmsListener 注解,参见第26.6节“注释驱动的侦听器端点 “ 更多细节。

用消息监听容器从 JMS 消息队列接收消息,并驱动被注入该消息的消息监听器。监听容器负责消息接收和分发到对应的监听器的所有线程。消息监听容器是 MDP 和消息提供者之间的一个中介,负责处理消息接收的注册、事务管理、资源获取与释放和异常转换等。这使得应用开发人员可以专注于开发和接收消息(可能的响应)相关的(复杂)业务逻辑,把和 JMS 基础框架有关的样板化的部分委托给框架处理。

有两个标准的 JMS 消息监听容器包含在 Spring 中,每一个都有它特殊的功能集。

SimpleMessageListenerContainer

这个消息监听容器是两种标准风格中比较简单的一个,它在启动时创建固定数量的 JMS 会话和消费者,使用标准的 JMS 方法MessageConsumer.setMessageListener()注册监听,并且让 JMS 提供者做监听回调。它不适于动态运行要求或者参与额外管理事务。兼容上,它与标准的 JMS 规范很近,但它通常情况下不兼容 Java EE 的 JMS 限制条件。

虽然SimpleMessageListenerContainer不允许参与外部管理的事务,但它确实支持原生 JMS 事务:只需将sessionTransacted标志切换为 true,或者在命名空间中将acknowledge属性设置为 transacted:监听器抛出的异常将会导致回滚,然后消息被重新传递。或者,考虑使用CLIENT_ACKNOWLEDGE模式,在异常的情况下提供重新传递,但没有使用事务会话,因此在事务协议中不包括任何其他会话操作(例如发送响应消息)。

DefaultMessageListenerContainer

这个消息监听容器用于大部分的案例中。与SimpleMessageListenerContainer相反的是,这个容器适于动态运行要求并且能参与额外管理事务。 在配置JtaTransactionManager的时候,每一个被接收的消息使用 XA 事务注册,因此可能利用 XA 事务语法处理。该监听容器在 JMS 提供者低要求、高级功能(如外部管理事务的参与)以及与 Java EE 环境的兼容性之间取得了良好的平衡。

容器缓存等级可以定制,注意当缓存不可用的时候,每一次消息接收,一个新的连接和新的会话就会被创建。使用高负载的非持久化订阅可能导致消息丢失,在这种情况下,确保使用合适的缓存等级。

当代理挂掉时,此容器也具备可恢复的能力。默认情况下,一个简单的BackOff实现会每5秒重试一次。可以为更细粒度的恢复选项指定自定义的BackOff实现,请参见ExponentialBackOff示例。

与同级的SimpleMessageListenerContainer一样,DefaultMessageListenerContainer支持原生 JMS 事务,并允许自定义确认模式。如果可行的话,强烈建议您使用外部管理的事务:即,如果你可以忍受在 JVM 挂掉的情况下偶尔会重复发送消息。业务逻辑中的自定义重复消息检测步骤可能涵盖这些情况,例如以业务实体的形式存在的检查或协议表检查。这样的安排将比任何其他方式显着更有效:用 XA 事务(通过使用JtaTransactionManager配置你的DefaultMessageListenerContainer)来包裹整个过程,覆盖了 JMS 消息的接收以及消息监听器中业务逻辑的执行(包括数据库操作等)。

26.2.5 事务管理

Spring 提供了一个JmsTransactionManager用于对 JMS ConnectionFactory做事务管理。这将允许 JMS 应用利用 Spring 的事务管理特性。第13章事务管理中所述的 Spring 的托管事务功能JmsTransactionManager在执行本地资源事务管理时将从指定的ConnectionFactory绑定一个ConnectionFactory/Session这样的配对到线程中。JmsTemplate会自动检测这样的事务资源,并对它们进行相应操作。

在 Java EE 环境中,ConnectionFactory会池化连接和会话,这样这些资源将会在整个事务中被有效地重复利用。在一个独立的环境中,使用 Spring 的SingleConnectionFactory时所有的事务将公用一个JMS 连接,但是每个事务将保留自己独立的会话。或者,请考虑使用具体提供者的池适配器,如 ActiveMQ 的PooledConnectionFactory类。

JmsTemplate也利用JtaTransactionManager和支持 XA 的 JMS ConnectionFactory一起来执行分布式事务。请注意,这需要使用 JTA 事务管理器以及正确的 XA 配置的ConnectionFactory!(检查您的 Java EE 服务/ JMS 提供者的文档。)

在使用 JMS API 从连接中创建会话时,通过托管和非托管事务环境重用代码可能会令人困惑。 这是因为 JMS API 只有一种工厂方法来创建会话,它需要对事务和确认模式赋值。在托管环境中,设置这些值是环境事务性基础架构的责任,因此供应商对 JMS 连接的包装器将忽略这些值。 在非托管环境中使用JmsTemplate时,可以通过使用属性sessionTransactedsessionAcknowledgeMode来指定这些值。 当与JmsTemplate一起使用PlatformTransactionManager时,模板将始终被赋予一个事务性 JMS 会话。

26.3 发送消息

JmsTemplate包含许多方便的方法来发送消息。有些发送方法可以使用 javax.jms.Destination对象指定目的地,也可以使用字符串在 JNDI 中查找目的地。没有目的地参数的发送方法使用默认的目的地。

import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.Session;

import org.springframework.jms.core.MessageCreator;
import org.springframework.jms.core.JmsTemplate;

public class JmsQueueSender {

	private JmsTemplate jmsTemplate;
	private Queue queue;

	public void setConnectionFactory(ConnectionFactory cf) {
		this.jmsTemplate = new JmsTemplate(cf);
	}

	public void setQueue(Queue queue) {
		this.queue = queue;
	}

	public void simpleSend() {
		this.jmsTemplate.send(this.queue, new MessageCreator() {
			public Message createMessage(Session session) throws JMSException {
				return session.createTextMessage("hello queue world");
			}
		});
	}
}

这个例子使用MessageCreator回调接口从提供的会话对象中创建一个文本消息,并且通过一个ConnectionFactory的引用来构造JmsTemplate。或者,提供了一个无参数的构造方法和connectionFactory,并可用于以 JavaBean 方式构建实例(使用 BeanFactory 或纯 Java 代码)。或者考虑从 Spring 的基类JmsGatewaySupport派生,它对 JMS 配置具有内置的 bean 属性。

方法send(String destinationName,MessageCreator creator)让你利用目的地的字符串名称发送消息。如果这些名称在 JNDI 中注册,则应将模板的destinationResolver属性设置为JndiDestinationResolver的一个实例。

如果创建了JmsTemplate并指定一个默认的目的地,那么send(MessageCreator c)会向该目的地发送消息。

26.3.1 使用消息转换器

为便于发送领域模型对象,JmsTemplate有多种以一个 Java 对象为参数并做为发送消息的数据内容。JmsTemplate里可重载的方法convertAndSendreceiveAndConvert将转换的过程委托给接口MessageConverter的一个实例。这个接口定义了一个简单的合约用来在 Java 对象和 JMS 消息间进行转换。缺省的实现SimpleMessageConverter支持StringTextMessagebyte[]BytesMesssage,以及java.util.MapMapMessage之间的转换。使用转换器,可以使你和你的应用关注于通过 JMS 接收和发送的业务对象而不用操心它是具体如何表达成 JMS 消息的。

目前的沙箱模型包括一个MapMessageConverter,它使用反射转换 JavaBean 和MapMessage。其他流行可选的实现方式包括使用已存在的 XML 编组的包(如 JAXB,Castor 或 XStream)来创建一个表示对象的TextMessage

为方便那些不能以通用方式封装在转换类里的消息属性、消息头和消息体的设置,通过MessagePostProcessor接口,你可以在消息被转换后并且在发送前访问该消息。下例展示了如何在java.util.Map已经转换成一个消息后更改消息头和属性。

public void sendWithConversion() {
	Map map = new HashMap();
	map.put("Name", "Mark");
	map.put("Age", new Integer(47));
	jmsTemplate.convertAndSend("testQueue", map, new MessagePostProcessor() {
		public Message postProcessMessage(Message message) throws JMSException {
			message.setIntProperty("AccountID", 1234);
			message.setJMSCorrelationID("123-00001");
			return message;
		}
	});
}

这将产生一个如下的消息格式:

MapMessage={
	Header={
		... standard headers ...
		CorrelationID={123-00001}
	}
	Properties={
		AccountID={Integer:1234}
	}
	Fields={
		Name={String:Mark}
		Age={Integer:47}
	}
}

26.3.2 SessionCallback和ProducerCallback

虽然 send 操作适用于许多常见的使用场景,但是有时你需要在一个 JMS 会话(Session) 或者MessageProducer上执行多个操作。接口SessionCallbackProducerCallback分别提供了 JMS Session 和 Session / MessageProducer 对。在JmsTemplate上的execute()方法执行这些回调方法。


转载自 并发编程网 - ifeve.com

相关文章
|
消息中间件 传感器 搜索推荐
|
消息中间件 Java Spring
spring ehcache jms activemq 分布式实现方案
spring ehcache jms activemq 分布式实现方案
115 0
|
消息中间件 存储 网络协议
Spring Boot与消息(JMS、AMQP、RabbitMQ)
1.概述。 大多应用中,可通过消息服务中间件来提升系统异步通信、扩展解耦能力。 消息服务中两个重要概念: 消息代理(message broker)和目的地(destination)。 当消息发送者发送
243 0
|
Java 数据库连接 应用服务中间件
分布式事务之Spring/JPA/JMS事务(二)
分布式事务之Spring/JPA/JMS事务
分布式事务之Spring/JPA/JMS事务(二)
|
XML 缓存 JSON
Spring MVC 阅读官方文档知识点总结
Spring MVC 阅读官方文档知识点总结
133 8
|
消息中间件 搜索推荐 Java
消息中间件JMS介绍、入门demo与spring整合
消息中间件JMS介绍、入门demo与spring整合
199 0
消息中间件JMS介绍、入门demo与spring整合
|
消息中间件 Java Spring
Spring消息之JMS.
一、概念 异步消息简介     与远程调用机制以及REST接口类似,异步消息也是用于应用程序之间通信的。     RMI、Hessian、Burlap、HTTP invoker和Web服务在应用程序之间的通信机制是同步的,即客户端应用程序直接与远程服务相交互,并且一直等到远程过程完成后才继续执行。
1205 0