使用Spring JMS

本节描述如何使用Spring的JMS组件。

使用JmsTemplate

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

使用JmsTemplate的代码只需要实现回调接口,这些接口为它们提供了明确定义的高级契约。 MessageCreator回调接口在给定由调用代码在JmsTemplate中提供的Session时创建消息。为了允许更复杂的JMS API使用,SessionCallback提供了JMS会话,ProducerCallback公开了SessionMessageProducer对。

JMS API公开了两种发送方法,一种采用传递模式、优先级和存活时间作为服务质量(QOS)参数,另一种不采用QOS参数并使用默认值。由于JmsTemplate有许多发送方法,设置QOS参数已经公开为bean属性,以避免在发送方法数量上的重复。同样,同步接收调用的超时值通过使用setReceiveTimeout属性设置。

一些JMS提供程序允许通过ConnectionFactory的配置在管理上设置默认的QOS值。这样做的效果是,调用MessageProducer实例的send方法(send(Destination destination, Message message))使用不同于JMS规范中指定的QOS默认值。为了提供一致的QOS值管理,因此必须明确启用JmsTemplate以使用其自己的QOS值,方法是将布尔属性isExplicitQosEnabled设置为true

JmsTemplate还公开了一个基本的请求-回复操作,允许发送消息并等待在操作的一部分创建的临时队列上收到回复。

JmsTemplate类的实例在配置后是线程安全的。这很重要,因为这意味着您可以配置一个JmsTemplate的单个实例,然后安全地将这个共享引用注入到多个协作者中。要明确的是,JmsTemplate是有状态的,因为它保持对ConnectionFactory的引用,但这种状态不是会话状态。
JmsMessagingTemplate是建立在 JmsTemplate之上的,并与消息抽象集成,即 org.springframework.messaging.Message。这使您可以以通用的方式创建要发送的消息。

连接

JmsTemplate需要引用一个ConnectionFactoryConnectionFactory是JMS规范的一部分,用作与JMS一起工作的入口点。它被客户端应用程序用作一个工厂,用于与JMS提供程序创建连接,并封装各种配置参数,其中许多是特定于供应商的,如SSL配置选项。

resource-ref。为了确保在EJB内部使用 JmsTemplate时使用这些功能,客户端应用程序应确保引用 ConnectionFactory的托管实现。

缓存消息资源

ConnectionFactory->Connection->Session->MessageProducer->send
ConnectionFactorySend操作之间,会创建和销毁三个中间对象。为了优化资源使用和提高性能,Spring提供了两个 ConnectionFactory的实现。

使用SingleConnectionFactory

ConnectionFactory接口的一个实现, SingleConnectionFactory,它在所有 createConnection()调用上返回相同的 Connection并忽略 close()调用。这对于测试和独立环境非常有用,因此可以将相同的连接用于可能跨任意数量事务的多个 JmsTemplate调用。 SingleConnectionFactory接受一个标准 ConnectionFactory的引用,通常来自JNDI。

使用CachingConnectionFactory

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

CachingConnectionFactory。

目的地管理

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

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

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

您还可以通过属性defaultDestination配置JmsTemplate的默认目的地。默认目的地用于不引用特定目的地的发送和接收操作。

消息监听容器

JMS消息在EJB世界中最常见的用途之一是驱动消息驱动的Bean(MDB)。Spring提供了一种解决方案,可以创建不将用户绑定到EJB容器的消息驱动POJO(MDP)。 (有关Spring对MDP支持的详细覆盖范围,请参见异步接收:消息驱动POJO。)自Spring Framework 4.1以来,端点方法可以使用@JmsListener进行注释 - 有关更多详细信息,请参见基于注释的监听器端点

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

Spring附带了两个标准的JMS消息监听容器,每个容器都有其专门的功能集。

使用SimpleMessageListenerContainer

这个消息监听容器是两种标准风格中更简单的一个。它在启动时创建固定数量的JMS会话和消费者,通过使用标准JMS MessageConsumer.setMessageListener()方法注册监听器,并由JMS提供程序执行监听器回调。这种变体不允许动态适应运行时需求或参与外部管理的事务。在兼容性方面,它与独立JMS规范的精神非常接近,但通常与Jakarta EE的JMS限制不兼容。

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

使用DefaultMessageListenerContainer

在大多数情况下使用此消息监听容器。与SimpleMessageListenerContainer相比,此容器变体允许动态适应运行时需求,并能够参与外部管理的事务。当配置了JtaTransactionManager时,每个接收的消息都将注册到XA事务中。因此,处理可以利用XA事务语义。此监听容器在对JMS提供程序的要求低、高级功能(如参与外部管理的事务)以及与Jakarta EE环境的兼容性之间取得了良好的平衡。

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

当代理服务器宕机时,此容器还具有可恢复的功能。默认情况下,简单的BackOff实现每五秒重试一次。您可以为更精细的恢复选项指定自定义BackOff实现。请参见ExponentialBackOff以获取示例。

与其同级(SimpleMessageListenerContainer)一样,DefaultMessageListenerContainer支持本机JMS事务,并允许自定义确认模式。如果对您的场景可行,强烈建议使用这种方法,而不是外部管理的事务 - 也就是说,如果您可以接受JVM死机时偶尔出现重复消息。在您的业务逻辑中自定义重复消息检测步骤可以覆盖这种情况 - 例如,以业务实体存在检查或协议表检查的形式。任何这样的安排都比另一种选择更有效:通过使用JtaTransactionManager配置您的DefaultMessageListenerContainer来包装整个处理,以覆盖JMS消息的接收以及消息监听器中的业务逻辑的执行(包括数据库操作等)。
默认的AUTO_ACKNOWLEDGE模式不提供适当的可靠性保证。当监听器执行失败时(由于提供程序在监听器调用后自动确认每条消息,不会将任何异常传播给提供程序)或当监听容器关闭时,消息可能会丢失(您可以通过设置acceptMessagesWhileStopping标志来配置此行为)。确保在需要可靠性的情况下使用事务会话(例如,用于可靠的队列处理和持久主题订阅)。

事务管理

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

在Jakarta EE环境中,ConnectionFactory会池化连接和会话实例,因此这些资源可以在事务之间高效地重用。在独立环境中,使用Spring的SingleConnectionFactory会导致共享的JMS Connection,每个事务都有自己独立的Session。或者,考虑使用提供程序特定的池适配器,例如ActiveMQ的PooledConnectionFactory类。

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

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