23. JMS (Java Message Service)
pdf版本请参见[http://www.docin.com/p-812929364.html]
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 |
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 |
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 |
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 |
prefetch |
The maximum number of messages to load into a single session. Note that raising this number might lead to starvation of concurrent consumers! |