Spring之JMS之接受消息

2 篇文章 0 订阅

23.4.1 同步接受

JMS一般是异步处理,也有可能同步消费消息。重载receive(..)方法提供了这个功能。在同步接受期间,调用线程会一直阻塞直到消息可用。这会是很危险的操作,因为调用线程可能随机发生阻塞。receiveTimeout属性指定了接收器在放弃等待一条消息前应该等待的时间。


23.4.2 异步接受---消息驱动 POJOs


需注意Spring通过使用@JmsListener也提供注解监听端并提供一个开放底层程序化注册端点。这是迄今为止设置异步接收器最方便的方式。


与EJB中MDB相似,JMS 消息使用消息驱动的POJO即MDP作为接收器。MDP的一个约束是必须要实现javax.jms.MessageListener接口。请注意这种情况,你的POJO在多线程下接受消息时,确保线程安全很重要。


下面是一个简单的MDP的实现:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. import javax.jms.JMSException;  
  2. import javax.jms.Message;  
  3. import javax.jms.MessageListener;  
  4. import javax.jms.TextMessage;  
  5.   
  6. public class ExampleListener implements MessageListener {  
  7.   
  8.     public void onMessage(Message message) {  
  9.         if (message instanceof TextMessage) {  
  10.             try {  
  11.                 System.out.println(((TextMessage) message).getText());  
  12.             }  
  13.             catch (JMSException ex) {  
  14.                 throw new RuntimeException(ex);  
  15.             }  
  16.         }  
  17.         else {  
  18.             throw new IllegalArgumentException("Message must be of type TextMessage");  
  19.         }  
  20.     }  
  21.   
  22. }  

只要实现了 MessageListener 接口,就该创建一个消息监听容器了。


找出下面的例子中如何在Spring中定义和配置一个消息监听容器的(这里是DefaultMessageListenerContainer


[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <!-- this is the Message Driven POJO (MDP) -->  
  2. <bean id="messageListener" class="jmsexample.ExampleListener" />  
  3.   
  4. <!-- and this is the message listener container -->  
  5. <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">  
  6.     <property name="connectionFactory" ref="connectionFactory"/>  
  7.     <property name="destination" ref="destination"/>  
  8.     <property name="messageListener" ref="messageListener" />  
  9. </bean>  

23.4.3 SessionAwareMessageListener接口


SessionAwareMessageListener是Spring指定的接口,用于提供与JMS MessageListener接口相同的功能,但是也提供给消息处理方法,借此从消息接受的对象中访问JMS会话。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package org.springframework.jms.listener;  
  2.   
  3. public interface SessionAwareMessageListener {  
  4.   
  5.     void onMessage(Message message, Session session) throws JMSException;  
  6.   
  7. }  

你可以选择性地使你的MDPs实现这个接口(参考标准的JMS  MessageListener 接口),如果你想你的MDPs能够响应任何接受的消息(使用 onMessage(Message, Session) 方法中的Session)。所有的Spring的消息监听容器支持MDPs实现 MessageListener 或者 SessionAwareMessageListener 接口。实现了 SessionAwareMessageListener 接口的类有点瑕疵,因为其与Spring耦合。是否选择使用这个接口完全取决于应用程序开发者或架构师。


请注意SessionAwareMessageListener接口的'onMessage(..)'方法抛出了JMSException。与标准的JMSMessageListener接口比较,当使用SessionAwareMessageListener接口时,客户端代码负责处理任何异常抛出。


23.4.4 MessageListenerAdapter


MessageListenerAdapter类是Spring异步消息支持中的最终组件:概括性说,其允许你暴露几乎任何类作为MDP(当然了也有一些约束)。


考虑下面的接口定义。注意到这个接口既不继承MessageListener,也不继承SessionAwareMessageListener接口,借助MessageListenerAdapter类,也可以用作MDP。也要注意到不同的消息处理方法根据不同的Message类型的内容(其可以接受和处理的额)如何强制性类型转换。


[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public interface MessageDelegate {  
  2.   
  3.     void handleMessage(String message);  
  4.   
  5.     void handleMessage(Map message);  
  6.   
  7.     void handleMessage(byte[] message);  
  8.   
  9.     void handleMessage(Serializable message);  
  10.   
  11. }  
  12. public class DefaultMessageDelegate implements MessageDelegate {  
  13.     // implementation elided for clarity...  
  14. }  

尤其是,上述的 MessageDelegate 接口的实现类(这里是 DefaultMessageDelegate 类)没有任何的JMS依赖。完全是一个POJO,通过以下配置转换为一个MDP。


[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <!-- this is the Message Driven POJO (MDP) -->  
  2. <bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">  
  3.     <constructor-arg>  
  4.         <bean class="jmsexample.DefaultMessageDelegate"/>  
  5.     </constructor-arg>  
  6. </bean>  
  7.   
  8. <!-- and this is the message listener container... -->  
  9. <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">  
  10.     <property name="connectionFactory" ref="connectionFactory"/>  
  11.     <property name="destination" ref="destination"/>  
  12.     <property name="messageListener" ref="messageListener" />  
  13. </bean>  

下面是另一个MDP的例子,其能处理JMS  TextMessage 消息的接受。注意消息处理方法如何调用'receive'( MessageListenerAdapter 类中消息处理方法名默认为 'handleMessage' ) ,但是它是可配置的(如下所示)。也注意到 'receive(..)' 方法如何强制类型转化为仅接受和响应JMS TextMessage 消息。


[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public interface TextMessageDelegate {  
  2.   
  3.     void receive(TextMessage message);  
  4.   
  5. }  
  6. public class DefaultTextMessageDelegate implements TextMessageDelegate {  
  7.     // implementation elided for clarity...  
  8. }  

相应的 MessageListenerAdapter 配置如下:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">  
  2.     <constructor-arg>  
  3.         <bean class="jmsexample.DefaultTextMessageDelegate"/>  
  4.     </constructor-arg>  
  5.     <property name="defaultListenerMethod" value="receive"/>  
  6.     <!-- we don't want automatic message context extraction -->  
  7.     <property name="messageConverter">  
  8.         <null/>  
  9.     </property>  
  10. </bean>  


请注意上述的'messageListener'接受了一个JMS 消息,而不是TextMessage类型,将抛出一个IllegalStateException异常。MessageListenerAdapter类的另一个功能是自动发送回一个响应消息,如果处理器方法返回一个有效值的话。看下面的接口和类:


[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public interface ResponsiveTextMessageDelegate {  
  2.   
  3.     // notice the return type...  
  4.     String receive(TextMessage message);  
  5.   
  6. }  
  7. public class DefaultResponsiveTextMessageDelegate implements ResponsiveTextMessageDelegate {  
  8.     // implementation elided for clarity...  
  9. }  

如果上述的 DefaultResponsiveTextMessageDelegate MessageListenerAdapter 联合使用,从执行的 'receive(..) 方法中返回一个非空值,将转换为一个 TextMessage 。结果 TextMessage 将在稍后发送到Destination(如果存在),其在JMS 的初始 Message 的Reply-To属性中定义,或者 MessageListenerAdapter 定义的默认的 Destination (如果已经配置了);如果没有发现 Destination ,则抛出异常 InvalidDestinationException (请注意这个异常将不被容忍,并将传送到调用回调)。


23..4.5 事物内处理消息


在一个事物中调用消息监听器仅仅需要重新配置监听容器。

定位资源事物可以通过监听容器定义的sessionTransacted标示激活。每个消息监听调用将在一个活性的JMS事物中执行,并在监听执行失败时,消息接受会回滚。发送一个响应消息(借助SessionAwareMessageListener)将是相同本地事物一部分,但是另外的资源操作(比如数据库访问)将分开操作。这通常需要在监听实现中进行多重的消息查找,囊括了这种情况,提交了数据库处理但是消息处理提交失败。


[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">  
  2.     <property name="connectionFactory" ref="connectionFactory"/>  
  3.     <property name="destination" ref="destination"/>  
  4.     <property name="messageListener" ref="messageListener"/>  
  5.     <property name="sessionTransacted" value="true"/>  
  6. </bean>  

对于外部管理事物中的多人参与情况,你需要配置一个事物管理器并使用一个监听容器,其支持外部事物管理:一般的是 DefaultMessageListenerContainer


对于XA事物参与配置的消息监听容器,你将想要配置一个JtaTransactionManager(默认地,委托给Java EE服务器的事物子系统)。注意到底层的JMS ConnectionFactory需要是XA-capable并注册你的JTA 事物协调器。


[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>  

之后 你只需要将其添加到你早期的容器配置中。容器就负责剩余的工作了:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">  
  2.     <property name="connectionFactory" ref="connectionFactory"/>  
  3.     <property name="destination" ref="destination"/>  
  4.     <property name="messageListener" ref="messageListener"/>  
  5.     <property name="transactionManager" ref="transactionManager"/>  
  6. </bean>  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值