使用Spring JMS
本节描述如何使用Spring的JMS组件。
使用JmsTemplate
JmsTemplate
类是JMS核心包中的中心类。它简化了JMS的使用,因为它在发送或同步接收消息时处理资源的创建和释放。
使用JmsTemplate
的代码只需要实现回调接口,这些接口为它们提供了明确定义的高级契约。 MessageCreator
回调接口在给定由调用代码在JmsTemplate
中提供的Session
时创建消息。为了允许更复杂的JMS API使用,SessionCallback
提供了JMS会话,ProducerCallback
公开了Session
和MessageProducer
对。
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
需要引用一个ConnectionFactory
。 ConnectionFactory
是JMS规范的一部分,用作与JMS一起工作的入口点。它被客户端应用程序用作一个工厂,用于与JMS提供程序创建连接,并封装各种配置参数,其中许多是特定于供应商的,如SSL配置选项。
resource-ref。为了确保在EJB内部使用
JmsTemplate
时使用这些功能,客户端应用程序应确保引用
ConnectionFactory
的托管实现。
缓存消息资源
ConnectionFactory->Connection->Session->MessageProducer->send
ConnectionFactory
和
Send
操作之间,会创建和销毁三个中间对象。为了优化资源使用和提高性能,Spring提供了两个
ConnectionFactory
的实现。
使用SingleConnectionFactory
ConnectionFactory
接口的一个实现,
SingleConnectionFactory
,它在所有
createConnection()
调用上返回相同的
Connection
并忽略
close()
调用。这对于测试和独立环境非常有用,因此可以将相同的连接用于可能跨任意数量事务的多个
JmsTemplate
调用。
SingleConnectionFactory
接受一个标准
ConnectionFactory
的引用,通常来自JNDI。
使用CachingConnectionFactory
CachingConnectionFactory
扩展了SingleConnectionFactory
的功能,并添加了对Session
、MessageProducer
和MessageConsumer
实例的缓存。初始缓存大小设置为1
。您可以使用sessionCacheSize
属性增加缓存会话的数量。请注意,实际缓存的会话数量比该数字更多,因为会话是基于它们的确认模式进行缓存的,因此当sessionCacheSize
设置为1时,可以有多达四个缓存的会话实例(每个确认模式一个)。MessageProducer
和MessageConsumer
实例在所属会话内缓存,并在缓存时考虑生产者和消费者的唯一属性。MessageProducers基于它们的目的地进行缓存。MessageConsumers基于由目的地、选择器、noLocal传递标志和持久订阅名称(如果创建持久性消费者)组成的键进行缓存。
CachingConnectionFactory。 |
目的地管理
目的地作为ConnectionFactory
实例,是JMS管理对象,可以存储和检索在JNDI中。在配置Spring应用程序上下文时,可以使用JNDI JndiObjectFactoryBean
工厂类或<jee:jndi-lookup>
来对对象对JMS目的地的引用执行依赖注入。然而,如果应用程序中有大量目的地或者JMS提供程序独有的高级目的地管理功能,则这种策略通常很繁琐。这些高级目的地管理的示例包括创建动态目的地或支持目的地的分层命名空间。 JmsTemplate
将目的地名称的解析委托给实现DestinationResolver
接口的JMS目的地对象。 DynamicDestinationResolver
是JmsTemplate
使用的默认实现,可解析动态目的地。还提供了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
类。
您还可以使用JmsTemplate
与JtaTransactionManager
和支持XA的JMS ConnectionFactory
执行分布式事务。请注意,这需要使用JTA事务管理器以及正确配置XA的ConnectionFactory。(请查阅您的Jakarta EE服务器或JMS提供程序的文档。)
在托管和非托管事务环境中重用代码时,使用JMS API从Connection
创建Session
可能会令人困惑。这是因为JMS API只有一个工厂方法用于创建Session
,并且需要为事务和确认模式提供值。在托管环境中,设置这些值是环境事务基础设施的责任,因此这些值会被供应商的JMS Connection包装器忽略。当您在非托管环境中使用JmsTemplate
时,可以通过属性sessionTransacted
和sessionAcknowledgeMode
指定这些值。当您使用PlatformTransactionManager
与JmsTemplate
时,模板始终会获得一个事务性的JMS Session
。