对于最新的稳定版本,请使用 Spring Framework 7.0.6!spring-doc.cadn.net.cn

使用 Spring JMS

本节介绍如何使用Spring的JMS组件。spring-doc.cadn.net.cn

使用 JmsTemplate

JmsTemplate 类是 JMS 核心包中的核心类。它简化了 JMS 的使用,因为它在发送或同步接收消息时会处理资源的创建和释放。spring-doc.cadn.net.cn

使用 JmsTemplate 的代码只需实现提供明确高级契约的回调接口。MessageCreator 回调接口在给定调用代码提供的 Session 时会创建一条消息,该消息位于 JmsTemplate 中。为了支持对 JMS API 更复杂的使用,SessionCallback 提供了 JMS 会话,ProducerCallback 暴露了一个 SessionMessageProducer 对。spring-doc.cadn.net.cn

JMS API 暴露了两种发送方法,一种接受传递模式、优先级和生存时间作为服务质量(QOS)参数,另一种不接受 QOS 参数并使用默认值。由于 JmsTemplate 有多种发送方法,设置 QOS 参数已被暴露为 bean 属性,以避免发送方法数量的重复。同样,同步接收调用的超时值通过使用 setReceiveTimeout 属性来设置。spring-doc.cadn.net.cn

一些JMS提供程序允许通过配置ConnectionFactory从管理上设置默认QOS值。这会导致对MessageProducer实例的send方法(send(Destination destination, Message message))的调用使用的QOS默认值与JMS规范中指定的不同。为了提供一致的QOS值管理,因此必须通过将布尔属性isExplicitQosEnabled设置为true来专门启用JmsTemplate以使用其自身的QOS值。spring-doc.cadn.net.cn

为方便起见,JmsTemplate 还提供了一个基本的请求-回复操作,允许在临时队列上发送消息并等待回复,该临时队列是在操作过程中创建的。spring-doc.cadn.net.cn

JmsTemplate 类的实例是线程安全的,一旦配置完成。这一点很重要,因为这意味着您可以配置一个 JmsTemplate 的实例,然后将这个共享引用安全地注入到多个协作对象中。明确地说,JmsTemplate 是有状态的,因为它维护对 ConnectionFactory 的引用,但此状态不是会话状态。

从Spring框架4.1开始,JmsMessagingTemplate建立在JmsTemplate之上,并提供了与消息抽象的集成——即org.springframework.messaging.Message。这使您能够以通用的方式创建要发送的消息。spring-doc.cadn.net.cn

连接

JmsTemplate 需要对 ConnectionFactory 的引用。 ConnectionFactory 是 JMS 规范的一部分,作为使用 JMS 的入口点。它由客户端应用程序用作与 JMS 提供程序建立连接的工厂,并封装了各种配置参数,其中许多是提供商特定的,例如 SSL 配置选项。spring-doc.cadn.net.cn

在 EJB 中使用 JMS 时,提供商会提供 JMS 接口的实现,以便它们能够参与声明式事务管理,并对连接和会话进行池化。为了使用此实现,Jakarta EE 容器通常要求您在 EJB 或 Servlet 的部署描述符中将 JMS 连接工厂声明为 resource-ref。为了确保在 EJB 中使用 JmsTemplate 时能利用这些功能,客户端应用程序应确保引用 ConnectionFactory 的受管实现。spring-doc.cadn.net.cn

缓存消息资源

标准 API 涉及创建许多中间对象。要发送一条消息,会执行以下“API”流程:spring-doc.cadn.net.cn

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

ConnectionFactorySend 操作之间,创建并销毁了三个中间对象。为了优化资源使用并提高性能,Spring 提供了 ConnectionFactory 的两种实现。spring-doc.cadn.net.cn

使用 SingleConnectionFactory

Spring 提供了 ConnectionFactory 接口的实现, SingleConnectionFactory,它在所有 createConnection() 调用中返回相同的 Connection,并忽略对 close() 的调用。这在测试和独立环境中很有用,以便可以将同一连接用于多个 JmsTemplate 调用,这些调用可能跨越任意数量的事务。SingleConnectionFactory 获取对标准 ConnectionFactory 的引用,通常来自 JNDI。spring-doc.cadn.net.cn

使用 CachingConnectionFactory

CachingConnectionFactory 扩展了 SingleConnectionFactory 的功能,并添加了 SessionMessageProducerMessageConsumer 实例的缓存。初始缓存大小设置为 1。您可以使用 sessionCacheSize 属性来增加缓存的会话数。请注意,实际缓存的会话数比该数字更多,因为会话是根据其确认模式进行缓存的,因此当 sessionCacheSize 设置为 1 时,最多可以有四个缓存的会话实例(每个确认模式一个)。MessageProducerMessageConsumer 实例在其所属的会话内进行缓存,并在缓存时考虑生产者和消费者独特的属性。MessageProducers 根据其目标地址进行缓存。MessageConsumers 根据由目标地址、选择器、noLocal 传递标志和持久订阅名称(如果创建持久化消费者)组成的键进行缓存。spring-doc.cadn.net.cn

用于临时队列和主题(TemporaryQueue/TemporaryTopic)的消息生产者和消息消费者永远不会被缓存。不幸的是,WebLogic JMS在其常规目标实现上实现了临时队列/主题接口,这会错误地表明其任何目标都不能被缓存。请在WebLogic上使用不同的连接池/缓存,或为WebLogic目的自定义CachingConnectionFactoryspring-doc.cadn.net.cn

目标管理

目标对象,作为 ConnectionFactory 实例,是 JMS 管理对象,您可以将其存储和从 JNDI 中检索。在配置 Spring 应用程序上下文时,可以使用 JNDI JndiObjectFactoryBean 工厂类或 <jee:jndi-lookup> 来对您的对象的 JMS 目标引用进行依赖注入。然而,如果应用程序中有大量目标对象,或者如果存在 JMS 提供商特有的高级目标管理功能,这种策略通常会很繁琐。此类高级目标管理的示例包括动态目标的创建或对目标分层命名空间的支持。JmsTemplate 将目标名称的解析委托给实现 DestinationResolver 接口的 JMS 目标对象。DynamicDestinationResolverJmsTemplate 默认使用的实现,并支持解析动态目标。还提供了一个 JndiDestinationResolver,用于作为包含在 JNDI 中的目标的服务定位器,并且可选地回退到 DynamicDestinationResolver 中包含的行为。spring-doc.cadn.net.cn

在JMS应用程序中,目标通常只能在运行时知道,因此在应用程序部署时无法被管理创建。这通常是因为在交互式系统组件之间有共享的应用程序逻辑,这些组件根据一个众所周知的命名约定在运行时创建目标。尽管动态目标的创建不属于JMS规范的一部分,但大多数提供商都提供了此功能。动态目标使用用户定义的名称进行创建,这使它们与临时目标区分开来,并且通常不会在JNDI中注册。用于创建动态目标的API因提供商而异,因为与目标相关的属性是提供商特定的。 然而,有时提供商会做出一个简单的实现选择,忽略JMS规范中的警告,并使用方法TopicSession createTopic(String topicName)QueueSession createQueue(String queueName)方法来创建具有默认目标属性的新目标。根据提供商的实现,DynamicDestinationResolver也可以创建物理目标,而不仅仅是解析一个目标。spring-doc.cadn.net.cn

布尔属性 pubSubDomain 用于配置 JmsTemplate,以便了解正在使用的 JMS 域。默认情况下,此属性的值为 false,表示将使用点对点域 Queues。此属性(由 JmsTemplate 使用)通过 DestinationResolver 接口的实现确定动态目标解析的行为。spring-doc.cadn.net.cn

您也可以通过属性 JmsTemplatedefaultDestination 配置为默认目标。默认目标是指不引用特定目标的发送和接收操作。spring-doc.cadn.net.cn

消息监听容器

在EJB世界中,JMS消息最常见的用途是驱动消息驱动 Bean(MDB)。Spring提供了一种解决方案,可以创建消息驱动的POJO(MDP),而不会将用户绑定到EJB容器。(有关Spring对MDP支持的详细内容,请参阅异步接收:消息驱动的POJO。)自Spring框架4.1以来,端点方法可以用@JmsListener进行注解——有关更多细节,请参阅基于注解的监听器端点spring-doc.cadn.net.cn

一个消息监听器容器用于从JMS消息队列接收消息并驱动注入到其中的MessageListener。监听器容器负责所有消息接收的线程处理,并将消息分发给监听器进行处理。消息监听器容器是MDP和消息提供者之间的中介,并负责注册以接收消息、参与事务、资源获取和释放、异常转换等。这使您可以编写与接收消息(并可能作出响应)相关的(可能是复杂的)业务逻辑,并将样板JMS基础设施问题委托给框架。spring-doc.cadn.net.cn

Spring 中包含了两种标准的 JMS 消息监听器容器,每种都有其专门的功能集。spring-doc.cadn.net.cn

使用 SimpleMessageListenerContainer

此消息监听器容器是两种标准类型中较简单的一种。它在启动时创建固定数量的JMS会话和消费者,使用标准JMS MessageConsumer.setMessageListener() 方法注册监听器,并由JMS提供者负责执行监听器回调。该变体不允许动态适应运行时需求,也无法参与外部管理的事务。从兼容性角度看,它非常贴近独立JMS规范的设计理念,但通常不兼容Jakarta EE对JMS的限制。spring-doc.cadn.net.cn

虽然 SimpleMessageListenerContainer 不允许参与外部管理的事务,但它支持本地 JMS 事务。要启用此功能,可以将 sessionTransacted 标志切换为 true,或者在 XML 命名空间中将 acknowledge 属性设置为 transacted。从您的监听器抛出的异常会导致回滚,并且消息会重新投递。或者,考虑使用 CLIENT_ACKNOWLEDGE 模式,它在发生异常时也提供重新投递,但不使用已交易的 Session 实例,因此不会将任何其他 Session 操作(如发送响应消息)包含在事务协议中。
默认的 AUTO_ACKNOWLEDGE 模式不提供适当的可靠性保证。 当监听器执行失败时消息可能会丢失(因为提供者在监听器调用后自动确认每条消息,没有异常可以传播到提供者)或者当监听器容器关闭时(可以通过设置 acceptMessagesWhileStopping 标志来配置)。如果需要可靠性,请使用事务性会话(例如,用于可靠队列处理和持久主题订阅)。

使用 DefaultMessageListenerContainer

此消息监听容器在大多数情况下使用。与 SimpleMessageListenerContainer 相比,这种容器变体能够根据运行时需求进行动态调整,并可参与外部管理的事务。 当配置了 JtaTransactionManager 时,每个接收到的消息都会注册到一个 XA 事务中。因此,处理过程可以利用 XA 事务语义。 该监听容器在对 JMS 提供程序的低要求、高级功能(例如参与外部管理的事务)以及与 Jakarta EE 环境的兼容性之间取得了良好平衡。spring-doc.cadn.net.cn

你可以自定义容器的缓存级别。请注意,当未启用缓存时,每次接收消息都会创建一个新的连接和一个新的会话。将此与高负载下的非持久化订阅结合使用可能导致消息丢失。在这种情况下,请确保使用适当的缓存级别。spring-doc.cadn.net.cn

此容器在消息代理关闭时也具有可恢复的功能。默认情况下,一个简单的 BackOff 实现每五秒重试一次。您可以指定一个自定义的 BackOff 实现以获得更精细的恢复选项。有关示例,请参阅 ExponentialBackOffspring-doc.cadn.net.cn

与其兄弟(SimpleMessageListenerContainer)类似, DefaultMessageListenerContainer 支持原生的 JMS 事务,并允许自定义确认模式。如果您的场景可行,建议优先使用内部管理的事务——也就是说,如果您可以接受在 JVM 崩溃时偶尔出现重复消息的情况。您可以在业务逻辑中自定义重复消息检测步骤来处理此类情况——例如,通过检查业务实体是否存在或检查协议表。任何此类安排都比另一种方法更高效:通过将您的 DefaultMessageListenerContainer 配置为使用 JtaTransactionManager,将整个处理过程包装在 XA 事务中,以涵盖 JMS 消息的接收以及消息监听器中的业务逻辑执行(包括数据库操作等)。
默认的 AUTO_ACKNOWLEDGE 模式不提供适当的可靠性保证。 当监听器执行失败时消息可能会丢失(因为提供者在监听器调用后自动确认每条消息,没有异常可以传播到提供者)或者当监听器容器关闭时(可以通过设置 acceptMessagesWhileStopping 标志来配置)。如果需要可靠性,请使用事务性会话(例如,用于可靠队列处理和持久主题订阅)。

事务管理

Spring 提供了一个 JmsTransactionManager,用于管理单个 JMS 的事务 ConnectionFactory。这使得 JMS 应用程序可以利用 Spring 的托管事务 功能,如在 数据访问章节的事务管理部分 所描述的那样。 JmsTransactionManager 执行本地资源事务,将指定的 ConnectionFactory 中的 JMS 连接/会话对绑定到线程。 JmsTemplate 会自动检测这些事务性资源并相应地操作它们。spring-doc.cadn.net.cn

在 Jakarta EE 环境中,ConnectionFactory 会池化 Connection 和 Session 实例, 从而在事务之间高效地重用这些资源。在独立环境中, 使用 Spring 的 SingleConnectionFactory 可以实现共享的 JMS Connection, 每个事务拥有自己独立的 Session。或者,也可以考虑使用特定提供商的池化适配器, 例如 ActiveMQ 的 PooledConnectionFactory 类。spring-doc.cadn.net.cn

您还可以将 JmsTemplateJtaTransactionManager 以及支持 XA 的 JMS ConnectionFactory 一起使用,以执行分布式事务。请注意,这需要使用 JTA 事务管理器以及正确配置了 XA 的 ConnectionFactory。(请查阅您的 Jakarta EE 服务器或 JMS 提供程序的文档。)spring-doc.cadn.net.cn

在使用JMS API从Connection创建Session时,在受管和不受管的事务环境中重用代码可能会令人困惑。这是因为JMS API只有一个工厂方法来创建Session,并且它需要事务和确认模式的值。在受管环境中,设置这些值是环境事务基础结构的责任,因此这些值会被JMS Connection的提供商包装器忽略。当在不受管环境中使用JmsTemplate时,可以通过使用属性sessionTransactedsessionAcknowledgeMode来指定这些值。当使用PlatformTransactionManagerJmsTemplate一起时,模板始终会获得一个事务性JMSSessionspring-doc.cadn.net.cn