23. JMS (Java Message Service)
23.1 介绍
Spring提供了一个JSM集成框架,简化了JMS API的使用。这点很像Spring对JDBC的集成。
JMS大致提供生产消息和消费消息两类功能。JmsTemplate类用来生产消息和同步接收消息【译注:接收消息也就是消费消息】。为了异步接收消息(异步接收消息类似于JavaEE的消息驱动Bean(Message-Driven Bean,MDB),Spring提供了一组消息监听器容器(messagelistener containers),用来创建多个消息驱动POJO(Message-Driven POJO,MDP)。
包org.springframework.jms.core
提供了
JMS
使用的核心功能。它包含了创建和释放资源的模板类,从而简化了
JMS
的使用,这点与
JdbcTemplate对JDBC起的作用相似。Spring模板类的通用设计原则是:(1)为常见操作提供辅助方法,(2)为了支持更复杂的使用,把处理任务的核心工作委托给用户实现的回调接口。JMS模板类的设计遵循相同的原则。这些类为发送消息和同步接收消息提供了各种便利的方法,同时向用户开放JMS的会话(Session)和消息生产者(Message
Producer)。
包org.spr
ingframework.jms.support提供转换JMSException的功能。具体为把checked类型的JMSException继承体系映射为unchecked的异常体系。任何JMS
Provider特有的javax.jms.JMSException(checked异常)的子类,都被封装为unchecked的UncategorizedJmsException异常。
包org.springframework.jms.support.converter提供了负责Java对象和JMS消息双向转换的MessageConverter抽象类。【译注:这个包内还有若干非抽象派生类】
包org.springframework.jms.support.destination提供了各种管理JMS destination的策略,比如提供了一个服务定位器(service locator)用来定位保存在JNDI中的destination。
最后,包org.springframework.jms.connection提供了一个适合于standalone应用的ConnectionFactory实现。它还包含了一个Spring为JMS提供的PlatformTransactionManager实现(名为JmsTransactionManager)。这允许把JMS作为一个事务资源与Spring的事务管理机制无缝整合。
【译注:文中出现了多次standalone,含义是把可执行代码作为单独的进程启动,而不是部署在应用服务器中】
23.2 使用Spring JMS
23.2.1 JmsTemplate
JmsTemplate类是JMS核心包的关键类。在发送消息和同步接收消息的时候,它负责资源的创建和释放,从而简化JMS的使用。
使用JmsTemplate的代码只需要实现回调接口,从而给予它们一个明确定义的高层约定(highlevel contract)。MessageCreator回调接口从JmsTemplate的调用代码接收一个Session对象,创建一条消息。为了允许JMS API更复杂的使用场景,SessionCallback回调接口提供了一个JMS Session对象作为参数,而ProducerCallback回调接口提供了一个Session对象和一个MessageProducer对象。
JMS API提供了两类发送消息的方法。一种把发送模式、优先级和time-to-live等服务质量(Quality of Service,QOS)作为参数。另一种不接受任何QOS参数,内部使用QOS的默认值。因为JmsTemplate中有很多发送方法,QOS参数被开放为bean属性,从而避免了在多个发送方法中反复设置。类似的,同步接收调用的超时时间,也是通过receiveTimeout属性设置的。
有些JMS Provider允许通过配置ConnectionFactory对象来管理式的(administratively)设定QOS默认值。这样做的效果就是调用MessageProducer的send方法send(Destination
destination, Message message)
将使用配置好的
QOS
值,而不是
JMS
规范指定的默认值。因此,为了提供一致的
QOS
管理,
JmsTemplate一定要被显式使能后,才能使用它自己的QOS值,这可通过把boolean类型的属性isExplicitQosEnabled设置为true完成。
Note |
一旦配置好了,JmsTemplate对象就是线程安全的。这点很重要,因为这意味着你可以先配置好一个JmsTemplate对象,然后把这个共享对象安全的注射到多个协作对象中。更清楚的讲,因为JmsTemplate维护了一个ConnectionFactory的引用,所以它是有状态的,但是这种状态不是对话状态。 |
23.2.2 Connections
JmsTemplate要求一个ConnectionFactory的引用。ConnectionFactory是JMS规范的一部分,是与JMS协同工作的入口点。客户端程序把它作为创建与JMS provider连接的工厂,它封装了各种配置参数,许多参数是各个vendor特有的,比如SSL配置选项。【译注:这里的vendor指JMS provider、application server provider等】
当在EJB中使用JMS,vendor提供了JMS实现,从而可以参与到声明式事务管理中,并且缓冲连接和会话。为了使用vendor提供的JMS实现,Java EE容器通常要求用户声明一个JMS连接工厂引用,它指向了EJB或Servlet的部署描述符中的资源。为了保证在EJB中JmsTemplate和以上特性协调工作,客户端程序需要确保JmsTemplate引用了托管的ConnectionFactory实现【译注:也就是资源描述符中定义的ConnectionFactory】。
缓存消息资源
标准JMS API涉及到创建很多中间对象。为了发送一个消息,下列对象和方法要依次被创建或调用:
ConnectionFactory->Connection->Session->MessageProducer->send
在ConnectionFactory和send操作之间,有三个中间对象被创建和销毁。为了优化资源使用和提高效率,Spring提供了两种IConnectionFactory的实现。
SingleConnectionFactory
Spring提供了一个ConnectionFactory接口的实现SingleConnectionFactory,它对所有的createConnection()调用都返回相同的Connection对象,并且忽略对close()的调用。这在测试和standalone环境中是有用的,它可以在跨越多个事务的多个JmsTemplate调用中,使用相同的连接。SingleConnectionFactory需要一个标准ConnectionFactory的引用,这个引用通常来自JNDI。
CachingConnectionFactory
CachingConnectionFactory扩展了SingleConnectionFactory的功能,增加了Session、MessageProducer和MessageConsumer的缓存。缓存大小初始为1,用户可以通过SessionCacheSize属性增加Session缓存的大小。需要注意的是,实际缓存的Session数量要比这个属性值大,这是因为Session是基于它们的应答模式(acknowledgmentmode)缓存的,因此当设置SessionCacheSize为1,最多可以缓存4个Session对象,即每个应答模式对应一个缓存对象。MessageProducer和MessageConsumer缓存在自己归属的Session中,缓存要基于这些生产者和消费者的唯一性属性。对于MessageProducer,缓存是基于它的destination。对于MessageConsumer,缓存是基于以下元素组成的联合键值:destination、selector、noLocal deliveryflag和持久化订阅名字(如果创建了持久化的消费者)。
23.2.3 Destination管理
Destination和ConnectionFactory一样,是JMS管理的对象,可以在JNDI中存储和获取。当配置一个Spring的application
context时,你需要使用JNDI工厂类JndiObjectFactoryBean/<jee:jndi-lookup>
,来解析对象中
JMSdestination
引用的依赖注入。然而,如果应用中有大量的
desntination
,或者应用涉及到
JMS
Provider
特有的
destination
高级管理特性,这种方法会很麻烦。这些高级管理特性的例子包括创建动态
destination
,或者支持分层的
destination
命名空间。
JmsTemplate把从destination名字到destination对象的解析任务委托给了DestinationResolver接口的实现。JmsTemplate使用的默认实现是DynamicDestinationResolver,它包含了解析动态destination的功能。而另一个实现JndiDestinationResolver,则首先按照JNDI中destination的service
locator的方式进行解析,解析失败后才执行与DynamicDestinationResolver相同的功能(但这步是可选的)。
很多时候,JMS应用中的destination只有在运行时才能知道,因此无法在部署应用的时候进行管理式的创建。这经常是因为交互的系统组件之间存在一个共享的应用逻辑,它负责在系统运行时,根据well-known的命名规范创建destination。虽然创建动态destination不是JMS规范的一部分,但是大部分vendor都提供了这项功能。动态destination被创建时,被赋予用户指定的名字,从而把它与临时destination区分。并且,动态destination通常也不在JNDI中注册。创建动态destination的API随着vendor的不同而不同,这是因为动态destination的属性也是vendor特有的。然而,有时候,vendor实现时进行的选择很简单,就是不管JMS规范中的警告,直接使用TopicSession中的createTopic(String
topicName)
方法,或者
QueueSession中的createQueue(String queueName)
方法,来创建一个新的带有默认属性的
destination
对象。
DynamicDestinationResolver可能也是创建而不是解析出一个destination实例,当然如何操作仍取决于vendor的实现。
boolean属性pubSubDomain用来配置JmsTemplate当前使用的JMS domain类型。该属性默认为false,表示使用point-to-pointdomain,也就是Queue。通过DestinationResolver接口的实现对象,JmsTemplate根据这个属性决定动态destination解析的行为。
你也可以通过defaultDestination属性给JmsTemplate配置一个默认destination。当进行发送和接收操作却没有指定特定destination时,将使用默认destination。
23.2.4 消息监听器容器
EJB中JMS消息最常见的用法是驱动MDB。Spring提供了一种创建MDP的方法,它不和任何EJB容器绑定。(关于Spring对MDP的支持,详见Section 23.4.2, “异步接收-消息驱动POJO” )
消息监听器容器(MessageListener Containers)用来从JMS消息队列接收消息,并驱动注入在容器中的MessageListener。消息监听器容器负责所有的消息接收,并把消息分发给监听器进行处理。消息监听器容器是MDP和消息提供者的中间人,进行注册操作然后接收消息,并参与到事务、资源获取和释放、异常转换等工作。这使你可以开发很复杂的应用逻辑,这些逻辑虽然与接收消息(有可能还需要发送响应)相关,但是却把通用的JMS样板代码委托给框架完成。
Spring提供了两个标准的JMS消息监听器容器,每个都有自己的特性。
SimpleMessageListenerContainer
这个消息监听器容器是两个标准实现中的比较简单的一个。它在启动的时候创建固定数目的JMSSession和消费者,调用标准JMS方法MessageConsumer.setMessageListener()注册监听器,并把调用监听器的任务丢给JMS Provider。它不允许动态适配运行时的命令,也不能参与外部管理的事务。在兼容性方面,它在思想上与standalone应用中的JMS规范很接近,但通常并不符合Java EE对JMS的限制。
DefaultMessageListenerContainer
这是大部分情况下会用到的消息监听器容器。和SimpleMessageListenerContainer比起来,这个容器确实允许动态适配运行时命令,并且也可以参与到外部管理的事务中。当用JtaTransactionManager配置时,每个接收到的消息都用XA事务注册,因此处理过程中就可以利用XA事务的语义。这个容器在以下几个方面进行了很好的平衡:(1)对JMS provider的低要求、(2)支持参与事务等高级功能、(3)兼容Java EE环境。
23.2.5 事务管理
Spring提供了一个JmsTransactionManager对象,用来为一个单独的JMS ConnectionFactory管理事务。这允许JMS应用程序利用Chapter 12,TransactionManagement提到的受管事务的特性。JmsTransactionManager执行本地资源事务,把来自于指定ConnectionFactory的JMS Connection/Session二元组绑定到线程。JmsTemplate自动检测到这些事务资源,并对它们执行相应操作。
在Java EE环境中,ConnectionFactory会缓存Connection和Session,因此这些资源可以在事务之间有效的重新利用。在standalone环境中,用户使用Spring的SingleConnectionFactory会导致一个共享的JMS Connection,同时,每次事务又有自己独立的Session。或者,用户也可以考虑采用JMS Provider特有的缓存适配机制,比如ActiveMQ的PooledConnectionFactory类。
JmsTemplate也可以与JtaTransactionManager、支持XA的JMS ConnectionFactory共同使用,来完成分布式事务。需要注意的是,这要求同时使用一个JTA事务管理器和一个配置好XA的ConnectionFactory。(这点请参照你的Java EE服务器的文档或者JMS Provider的文档)
当使用JMS API利用Connection创建Session时,在受管事务环境和非受管事务环境之间重用代码会导致一些混乱。这是因为JMS
API只有一个创建Session的工厂方法,它要求输入事务模式和应答模式(acknowledgementmode)。在受管环境下,环境的事务框架负责设置这些值,因此vendor提供的JMS
Connection的包装对象就忽略这些数值。当在非受管环境下使用JmsTemplate时,用户可以通过sessionTransacted
和
sessionAcknowledgeMode
属性来设置这些值。当和
JmsTemplate一起使用PlatformTransactionManager,JmsTemplate总是会被指定一个事务性的JMS
Session。
23.3 发送消息
JmsTemplate包含了很多发送消息的便利方法。有的发送方法要求通过javax.jms.Destination对象指定destination,有的发送方法要求通过JNDI查找字符串指定destination,还有的发送方法不接受任何destination参数,它使用默认的destination。下面是一个向queue发送消息的例子(使用1.0.2实现)。【译注:”1.0.2”,原文如此,不知何意】
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回调根据Session创建文本消息。代码在创建JmsTemplate对象时,传入了一个ConnectionFactory引用。或者,也可以采用不包含任何参数的构造函数,而采用JavaBean的风格构造对象(使用BeanFactory或者原始Java代码)。或者,考虑直接从Spring的JmsGatewaySupport基类派生,它为配置JMS准备了相关的bean属性。
方法send(String destinationName, MessageCreatorcreator)让你通过destination的字符串名字发送消息。如果这些名字是在JNDI中注册的,你应该把JmsTemplate的destinationResolver属性设置为JndiDestinationResolver对象。
如果你创建JmsTemplate并指定了默认的destination,send(MessageCreator c)会向这个默认destination发送消息。
23.3.1 使用消息转换器
为了方便发送domain模型对象,JmsTemplate有各种接收一个Java对象作为消息内容的send方法。JmsTemplate中的overloaded方法convertAndSend()
和receiveAndConvert()
,委托
MessageConverter接口完成从
Java
对象到消息对象的转换。
MessageConverter接口定义了Java对象和JMS消息之间转换的简单约定。这个接口的默认实现类SimpleMessageConverter支持在String和TextMessage之间、byte[]和BytesMesssage之间、java.util.Map和MapMessage之间的转换。通过使用这些converter类,用户和用户代码可以只关心被发送和接收的业务对象,而无需关心业务对象如何表示为JMS消息等细节问题。
沙箱中目前提供了一个MapMessageConverter,它使用反射完成JavaBean和MapMessage之间的转换。用户自行实现MessageConverter
,
可以采用XML序列化包(比如JAXB、Castor、XMLBeans或者XStream)来创建TextMesage,这个TextMessage就代表原来的对象。
【译注:这里的沙箱原文为sandbox,其实是指以下代码,与通常安全相关的沙箱没有关系。https://src.springframework.org/svn/spring-maintenance/trunk/sandbox/src/org/springframework/jms/MapMessageConverter.java】
为设置消息对象的属性、消息头和不能封装到converter类的消息体,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}
}
}
23.3.2 SessionCallback和ProducerCallback
虽然发送操作涵盖了常见的使用场景,但是有时你仍然需要对JMSSession对象或者MessageProducer对象执行多次操作。
SessionCallback
和ProducerCallback
分别公开了
JMS
Session
对象和
Session
/MessageProducer
二元组。
JmsTemplate的execute()
方法执行这些回调接口。
23.4 接收消息
23.4.1 同步接收
虽然JMS主要用于异步处理,它仍然支持同步接收消息。它的overloaded的receive(..)
方法提供了这项功能。同步接收过程中,调用线程一直阻塞到有可用的消息。因为调用线程可能无限期被阻塞,因此这种操作很危险。属性
receiveTimeout指定了最多可以等待多长时间。
23.4.2 异步接收-消息驱动POJO
与EJB世界的MDB风格类似, MDP是JMS消息的接收器。MDP的一个限制(但是还请参看下文对MessageListenerAdapter的描述)是,它必须实现javax.jms.
MessageListener接口。另外还需注意,如果你的
POJO
会在多线程下接收消息,请确保你的实现是线程安全的。
下面是一个MDP实现的简单例子。
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
public class ExampleListener implements MessageListener {
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
System.out.println(((TextMessage) message).getText());
}
catch (JMSException ex) {
throw new RuntimeException(ex);
}
}
else {
throw new IllegalArgumentException("Message must be of type TextMessage");
}
}
}
实现了MessageListener
后
,就可以创建消息监听器容器了。
下面是一个定义和配置Spring自带的消息监听器容器的例子(例子中配置的容器是DefaultMessageListenerContainer)。
<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="jmsexample.ExampleListener" />
<!-- and this is the message listener container -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener" />
</bean>
请参考Spring中各个消息监听器容器的Javadoc,它们描述了各个实现的所有特性。
23.4.3 SessionAwareMessageListener接口
SessionAwareMessageListener
接口是
Spring
特有的,它与
JMS
的
MessageListener接口类似,但它提供的消息处理方法允许实现代码访问相关的
Session
对象。
package org.springframework.jms.listener;
public interface SessionAwareMessageListener {
void onMessage(Message message, Session session) throws JMSException;
}
你可以让你的MDP实现这个接口(而不是标准的JMSMessageListener
),
从让你的MDP能够使用onMessage(Message,
Session)
中提供的
Session
对象,响应任何接收到的消息。任何实现了
MessageListener
或者
SessionAwareMessageListener
接口的
MDP
,都被
Spring
提供的消息监听器容器支持。但是实现
SessionAwareMessageListener
的
MDP
就和
Spring
绑定了。如何选择取决于应用开发者或者架构师。
需要注意的是,SessionAwareMessageListener
接口的
onMessage(..)
方法抛出
JMSException异常。与标准的MessageListener
接口不同,当使用
SessionAwareMessageListener
接口时,客户代码负责处理任何抛出的异常。
23.4.4 MessageListenerAdapter
MessageListenerAdapter类是Spring提供的最后一个支持异步消息的组件。简而言之,它允许你把任何类暴漏为MDP(当然有一些限制)。
请看下面的接口定义。虽然接口既没有继承MessageListener
,也没有继承
SessionAwareMessageListener,它仍然可以通过
MessageListenerAdapter被当作
MDP
使用。另外还请注意,
在接收和处理的消息类型方面,各个消息处理方法是强类型的。
public interface MessageDelegate {
void handleMessage(String message);
void handleMessage(Map message);
void handleMessage(byte[] message);
void handleMessage(Serializable message);
}
public class DefaultMessageDelegate implements MessageDelegate {
// implementation elided for clarity...
}
尤其请注意,以上MessageDelegate
的
实现(DefaultMessageDelegate类)与JMS是完全不相关的。它是一个真正的POJO,但我们会用下面的配置把它转换为MDP。
<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="jmsexample.DefaultMessageDelegate"/>
</constructor-arg>
</bean>
<!-- and this is the message listener container... -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener" />
</bean>
下面是另一个MDP的例子,它只能处理JMS TextMessage类型的消息。请注意消息
处理方法的名字是
‘receive‘
(
MessageListenerAdapter中消息处理方法的名字默认为
‘handleMessage‘
),但它是可以配置的(下面就会看到)。还请注意,
‘receive(..)‘
是强类型的,它只能接收和响应
JMS
TextMessage
类型的消息。
public interface TextMessageDelegate {
void receive(TextMessage message);
}
public class DefaultTextMessageDelegate implements TextMessageDelegate {
// implementation elided for clarity...
}
以下是服务对象MessageListenerAdapter的配置。
<bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="jmsexample.DefaultTextMessageDelegate"/>
</constructor-arg>
<property name="defaultListenerMethod" value="receive"/>
<!-- we don‘t want automatic message context extraction -->
<property name="messageConverter">
<null/>
</property>
</bean>
如果以上‘messageListener‘接收到了一个不是
TextMessage
类型的
JMS
Message
,
就会被抛出IllegalStateException异常(后面会被swallow掉)。MessageListenerAdapter类的另一个功能是,如果处理函数返回了非void值,它能够自动发回一个响应Message
。请看下面的接口和类:
public interface ResponsiveTextMessageDelegate {
// notice the return type...
String receive(TextMessage message);
}
public class DefaultResponsiveTextMessageDelegate implements ResponsiveTextMessageDelegate {
// implementation elided for clarity...
}
如果以上DefaultResponsiveTextMessageDelegate和MessageListenerAdapter一起使用,‘receive(..)‘
方法
返回的任何非null值都会被转换为TextMessage
(默认配置情况下)。转换后的
TextMessage
对象会被依次尝试发送给以下
Destination
:(
1
)原始
TextMessage
对象的
JMS
Reply-To
属性指定的
Destination
,(
2
)
MessageListenerAdapter中的默认Destination对象。如果以上Destination对象都为null,则抛出InvalidDestinationException异常(这个异常不会被swallow,而是会在调用堆栈中传播)。
23.4.5 事务内处理消息
在事务内调用消息监听器只需要重新配置监听器容器。
激活本地资源事务只需要设置监听器容器的sessionTransacted
标志。每次对消息监听器的调用都会在一个活动的
JMS
事务中进行,如果监听器执行失败,则回滚消息接收状态。通过
SessionAwareMessageListener
发送一个响应消息也是同一个本地事务的一部分,但是任何其他的资源操作(比如数据库访问)都会独立执行。这通常要求监听器在实现上检测重复消息,处理数据库处理事务提交成功、而消息处理事务提交失败的情况。
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener"/>
<property name="sessionTransacted" value="true"/>
</bean>
为了参与外部管理事务,你需要配置一个事务管理器,并使用一个支持外部管理事务的监听器容器,这通常是DefaultMessageListenerContainer。
为配置一个参与XA事务的消息监听器容器,你需要配置一个JtaTransactionManager(它默认代理给Java EE服务器的事务子系统)。请注意,底层的JMSConnectionFactory需要支持XA,并且被注册到JTA事务协调者中(请查看Java EE服务器JNDI资源配置的相关文档)。这允许消息接收和数据库操作(只是一个例子)在同一个事务内执行(具有统一的提交语义,但是需要额外的XA事务日志开销)。
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
然后你只需要把它加到我们前面的容器配置中。容器会负责剩下的工作。
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener"/>
<property name="transactionManager" ref="transactionManager"/>
</bean>
23.5 支持JCA消息端点(JCA MessageEndpoints)
从2.5版本开始,Spring还支持基于JCA的MessageListener
容器。
JmsMessageEndpointManager类会自动根据provider的ResourceAdapter
类名字,确定
ActivationSpec
的类名字。因此,通常只需要提供
Spring
的通用
JmsActivationSpecConfig配置,如下例所示。
<bean class="org.springframework.jms.listener.endpoint.JmsMessageEndpointManager">
<property name="resourceAdapter" ref="resourceAdapter"/>
<property name="activationSpecConfig">
<bean class="org.springframework.jms.listener.endpoint.JmsActivationSpecConfig">
<property name="destinationName" value="myQueue"/>
</bean>
</property>
<property name="messageListener" ref="myMessageListener"/>
</bean>
或者,你也可以为JmsMessageEndpointManager对象,指定一个ActivationSpec
对象
。ActivationSpec
对象也可以通过
JNDI
查找获得(使用
<jee:jndi-lookup>
)。
<bean class="org.springframework.jms.listener.endpoint.JmsMessageEndpointManager">
<property name="resourceAdapter" ref="resourceAdapter"/>
<property name="activationSpec">
<bean class="org.apache.activemq.ra.ActiveMQActivationSpec">
<property name="destination" value="myQueue"/>
<property name="destinationType" value="javax.jms.Queue"/>
</bean>
</property>
<property name="messageListener" ref="myMessageListener"/>
</bean>
通过使用ResourceAdapterFactoryBean,目标ResourceAdapter
也可以按如下例进行本地配置。
<bean id="resourceAdapter" class="org.springframework.jca.support.ResourceAdapterFactoryBean">
<property name="resourceAdapter">
<bean class="org.apache.activemq.ra.ActiveMQResourceAdapter">
<property name="serverUrl" value="tcp://localhost:61616"/>
</bean>
</property>
<property name="workManager">
<bean class="org.springframework.jca.work.SimpleTaskWorkManager"/>
</property>
</bean>
例子中的WorkManager也可以指向一个与环境相关的线程池,这通常是通过
SimpleTaskWorkManager的asyncTaskExecutor属性来设置。如果你碰巧使用多个ResourceAdapter
实例
,考虑为你所有的实例定义一个共享线程池。
在某些环境下(比如WebLogic 9或者更高版本),可能整个ResourceAdapter
对象都包含在
JNDI
中(使用
<jee:jndi-lookup>
)。此时,基于
Spring
的消息监听器可以通过服务器内置的
WorkManager
对象,与服务器内的
ResourceAdapter
交互。
请参考JmsMessageEndpointManager、JmsActivationSpecConfig和ResourceAdapterFactoryBean的JavaDoc,以获取更多信息。
Spring还提供一个不与JMS绑定的、通用的JCM消息端点管理器org.springframework.jca.endpoint.GenericMessageEndpointManager。这个组件允许使用任何类型的消息监听器(比如CCI MessageListener),允许使用任何特定provider的ActivationSpec对象。请参照你的JCA provider的文档以确定你的connector的实际功能,请参照GenericMessageEndpointManager的JavaDoc确定Spring相关的详细配置信息。
Note |
JCA-based message endpoint management is very analogous to EJB 2.1 Message-Driven Beans; it uses the same underlying resource provider contract. Like with EJB 2.1 MDBs, any message listener interface supported by your JCA provider can be used in the Spring context as well. Spring nevertheless provides explicit ‘convenience‘ support for JMS, simply because JMS is the most common endpoint API used with the JCA endpoint management contract. |
23.6 支持JMS命名空间
Spring2.5引入了一个XML命名空间以简化JMS配置。为了使用这个JMS命名空间的元素,你需要引用以下JMSschema:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd">
<!-- <bean/> definitions here -->
</beans>
这个命名空间包含两个*元素<listener-container/>
和<jca-listener-container/>
,它们都包含一个或多个
<listener/>
子元素。下面是配置两个监听器的基础例子。
<jms:listener-container>
<jms:listener destination="queue.orders" ref="orderService" method="placeOrder"/>
<jms:listener destination="queue.confirmations" ref="confirmationLogger" method="log"/>
</jms:listener-container>
以上例子等价于创建两个不同的监听器容器bean定义,和创建两个不同的MessageListenerAdapter的bean定义,这与Section 23.4.4,“The MessageListenerAdapter”中描述的一致。除了上面例子中的元素属性,listener元素还有其他可选属性。下表描述了所有可选属性。
Table 23.1. Attributesof the JMS<listener>
element
Attribute |
Description |
id |
A bean name for the hosting listener container. If not specified, a bean name will be automatically generated. |
destination (required) |
The destination name for this listener, resolved through the |
ref (required) |
The bean name of the handler object. |
method |
The name of the handler method to invoke. If the
|
response-destination |
The name of the default response destination to send response messages to. This will be applied in case of a request message that does not carry a "JMSReplyTo" field. The type of this destination will be determined by the listener-container‘s "destination-type" attribute. Note: This only applies to a listener method with a return value, for which each result object will be converted into a response message. |
subscription |
The name of the durable subscription, if any. |
selector |
An optional message selector for this listener. |
<listener-container/>
元素还支持一些可选属性。这包括自定义各种策略(比如
taskExecutor
和
destinationResolver)、基础性JMS设置和资源引用等。使用这些属性,既获得了命名空间的便利性,又能高度自定义监听器容器。
<jms:listener-container connection-factory="myConnectionFactory"
task-executor="myTaskExecutor"
destination-resolver="myDestinationResolver"
transaction-manager="myTransactionManager"
concurrency="10">
<jms:listener destination="queue.orders" ref="orderService" method="placeOrder"/>
<jms:listener destination="queue.confirmations" ref="confirmationLogger" method="log"/>
</jms:listener-container>
下表包含了所有的属性。AbstractMessageListenerContainer和它的非抽象子类的class级别的JavaDoc,包含了每个属性的详细信息。JavaDoc还讨论了事务选择和重传递消息的场景。
Table 23.2. Attributesof the JMS<listener-container>
element
Attribute |
Description |
container-type |
The type of this listener container. Available options are: |
connection-factory |
A reference to the JMS |
task-executor |
A reference to the Spring |
destination-resolver |
A reference to the |
message-converter |
A reference to the |
destination-type |
The JMS destination type for this listener:
|
client-id |
The JMS client id for this listener container. Needs to be specified when using durable subscriptions. |
cache |
The cache level for JMS resources: |
acknowledge |
The native JMS acknowledge mode: |
transaction-manager |
A reference to an external |
concurrency |
The number of concurrent sessions/consumers to start for each listener. Can either be a simple number indicating the maximum number (e.g. "5") or a range indicating the lower as well as the upper limit (e.g. "3-5"). Note that a specified minimum is just a hint and might be ignored at runtime. Default is 1; keep concurrency limited to 1 in case of a topic listener or if queue ordering is important; consider raising it for general queues. |
prefetch |
The maximum number of messages to load into a single session. Note that raising this number might lead to starvation of concurrent consumers! |
通过”jms” schema支持来配置基于JCA的监听器容器非常相似。
<jms:jca-listener-container resource-adapter="myResourceAdapter"
destination-resolver="myDestinationResolver"
transaction-manager="myTransactionManager"
concurrency="10">
<jms:listener destination="queue.orders" ref="myMessageListener"/>
</jms:jca-listener-container>
下表是可用的JCA配置选项:
Table 23.3. Attributesof the JMS<jca-listener-container/>
element
Attribute |
Description |
resource-adapter |
A reference to the JCA |
activation-spec-factory |
A reference to the |
destination-resolver |
A reference to the |
message-converter |
A reference to the |
destination-type |
The JMS destination type for this listener:
|
client-id |
The JMS client id for this listener container. Needs to be specified when using durable subscriptions. |
acknowledge |
The native JMS acknowledge mode: |
transaction-manager |
A reference to a Spring JtaTransactionManager or a |
concurrency |
The number of concurrent sessions/consumers to start for each listener. Can either be a simple number indicating the maximum number (e.g. "5") or a range indicating the lower as well as the upper limit (e.g. "3-5"). Note that a specified minimum is just a hint and will typically be ignored at runtime when using a JCA listener container. Default is 1. |
prefetch |
The maximum number of messages to load into a single session. Note that raising this number might lead to starvation of concurrent consumers! |
Spring Framework Reference Documentation 3.2.8.RELEASE 第23章中文翻译,布布扣,bubuko.com
Spring Framework Reference Documentation 3.2.8.RELEASE 第23章中文翻译