Java Message Service :Spring POJO (MDP)与EJB3的Java Message Driven (MDB)

来源:互联网 发布:济南网络电视台直播 编辑:程序博客网 时间:2024/06/05 06:06

Spring提供了一个用于简化JMS API使用的抽象框架,并且对用户屏蔽了JMS API中1.0.2和1.1版本的差异。

JMS的功能大致上分为两块,叫做消息制造和消息消耗。JmsTemplate 用于制造消息和同步消息接收。和Java EE的事件驱动Bean风格类似,对于异步接收消息,Spring提供了一些消息侦听容器来创建消息驱动的POJO(MDP)。

org.springframework.jms.core 包提供使用JMS的核心功能。 就象为JDBC提供的JdbcTemplate 一样,它提供了JMS模板类来处理资源的创建和释放以简化JMS的使用。Spring模板类的公共设计原则就是通过提供助手方法去执行公共的操作,并将实际的处理任务委派到用户实现的回调接口上,从而完成更复杂的操作。JMS模板也遵循这样的设计原则。这些类提供众多便利的方法来发送消息、同步接收消息、使用户可以接触到JMS session和消息产生者。

org.springframework.jms.support 包提供JMSException的转换功能。它将受控的 JMSException异常层次转换到一个对应的非受控异常层次。任何受控javax.jms.JMSException异常的子类都被包装在非受控UncategorizedJmsException异常里。

org.springframework.jms.support.converter 包提供一个 MessageConverter 用来抽象Java对象和JMS消息之间的转换操作。

org.springframework.jms.support.destination 包为管理JMS目的地提供多种策略,例如为存储在JNDI中的目的地提供一个服务定位器。

最后,org.springframework.jms.connection 包提供一个适合在独立应用中使用的ConnectionFactory 的实现。它还为JMS提供了一个Spring的 PlatformTransactionManager 的实现(现在叫做 JmsTransactionManager)。这样可以把JMS作为一个事务资源无缝地集成到Spring的事务管理机制中去。

使用Spring JMS

JmsTemplate  类有两个实现方式。JmsTemplate 类使用JMS 1.1的API,而子类JmsTemplate102 使用了JMS 1.0.2的API。

使用 JmsTemplate 的代码只需要实现规范中定义的回调接口。 MessageCreator 回调接口通过JmsTemplate中调用代码提供的Session来创建一条消息。然而,为了允许更复杂的JMS API应用,回调接口SessionCallback 为用户提供JMS session,并且回调接口 ProducerCallback 将Session和MessageProducer对显露给用户。

JMS API有两种发送方法,一种采用发送模式、优先级和存活时间作为服务质量(QOS)参数,另一种使用无需QOS参数的缺省值方法。由于在 JmsTemplate 中有许多种发送方法,QOS参数通过bean的属性方式进行设置,从而避免在多种发送方法中重复。同样,使用 setReceiveTimeout 属性值来设置同步接收调用的超时值。

某些JMS供应者允许通过ConnectionFactory的配置来设置缺省的QOS值。这样在调用 MessageProducer 的发送方法send(Destination destination, Message message) 时会使用那些不同的QOS缺省值,而不是JMS规范中定义的值。所以,为了提供对QOS值的一致管理,JmsTemplate必须通过设置布尔值属性isExplicitQosEnabled 为true,使它能够使用自己的QOS值。


JmsTemplate 类的实例 一经配置便是线程安全 的。 这很重要,因为这意味着你可以配置一个JmsTemplate 的单例,然后把这个 共享的 引用安全的注入多个协作的对象中。 要清楚一点,JmsTemplate 是有状态的,因为它维护了ConnectionFactory 的引用,但这个状态时不是会话状态。

连接工厂

JmsTemplate 需要一个对 ConnectionFactory 的引用。ConnectionFactory 是JMS规范的一部分,并且是使用JMS的入口。客户端应用通常用它作工厂配合JMS提供者去创建连接,并封装许多和供应商相关的配置参数,例如SSL的配置选项。

当在EJB里使用JMS时,供应商会提供JMS接口的实现,这样们可以参与声明式事务管理并提供连接池和会话池。 为了使用这个JMS实现,Java EE容器通常要求你在EJB或servlet部署描述符中声明一个JMS连接工厂做为resource-ref。 为确保可以在EJB内使用 JmsTemplate 的这些特性,客户应用应当确保它引用了被管理的ConnectionFactory实现。

Spring提供了一个 ConnectionFactory 接口的实现,SingleConnectionFactory,它将在所有的createConnection 调用中返回一个相同的 Connection,并忽略所有对close的调用。这在测试和独立环境中相当有用,因为多个 JmsTemplate 调用可以使用同一个连接以跨越多个事务。SingleConnectionFactory 通常引用一个来自JNDI的标准ConnectionFactory

目的地管理

和连接工厂一样,目的地是可以在JNDI中存储和获取的JMS管理的对象。配置一个Spring应用上下文时,可以使用JNDI工厂类 JndiObjectFactoryBean 把对你对象的引用依赖注入到JMS目的地中。然而,如果在应用中有大量的目的地,或者JMS供应商提供了特有的高级目的地管理特性,这个策略常常显得很麻烦。创建动态目的地或支持目的地的命名空间层次就是这种高级目的地管理的例子。JmsTemplate 将目的地名称到JMS目的地对象的解析委派给DestinationResolver 接口的一个实现。JndiDestinationResolverJmsTemplate 使用的默认实现,并且提供动态目的地解析。同时 JndiDestinationResolver 作为JNDI中的目的地服务定位器,还可选择回退去使用 DynamicDestinationResolver 中的行为。

经常见到一个JMS应用中使用的目的地在运行时才知道,因此,当部署一个应用时,它不能用可管理的方式创建。这是经常发生的,因为在互相作用的系统组件间有些共享应用逻辑会在运行的时候按照共同的命名规范创建消息目的地。虽然动态创建目的地不是JMS规范的一部分,但是大多数供应商已经提供了这个功能。用户为动态创建的目的地定义和临时目的地不同的名字,并且通常不被注册到JNDI中。不同供应商创建动态消息目的地所使用的API差异很大,因为和目的地相关的属性是供应商特有的。然而,有时由供应商会作出一个简单的实现选择-忽略JMS规范中的警告,使用TopicSession 的方法 createTopic(String topicName) 或者QueueSession 的方法 createQueue(String queueName) 来创建一个带默认值属性的新目的地。依赖于供应商的实现,DynamicDestinationResolver 也可能创建一个物理上的目的地,而不再仅仅是一个解析。

布尔属性 pubSubDomain 用来配置 JmsTemplate 使用什么样的JMS域。这个属性的默认值是false,使用点到点的域,也就是队列。在1.0.2的实现中,这个属性值用来决定JmsTemplate 将消息发送到一个 Queue 还是一个Topic。这个标志在1.1的实现中对发送操作没有影响。然而,在这两个JMS版本中,这个属性决定了通过接口DestinationResolver 的实现来决定如何解析动态消息目的地。

你还可以通过属性 defaultDestination 配置一个带有默认目的地的 JmsTemplate。不指明目的地的发送和接受操作将使用该默认目的地。

消息侦听容器

SimpleMessageListenerContainer

DefaultMessageListenerContainer

ServerSessionMessageListenerContainer

发送消息

package com.ccl.jms;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.JmsTemplate;import org.springframework.jms.core.JmsTemplate102;import org.springframework.jms.core.MessageCreator;/** * <p> * 发送消息 * </p> *  * @author changlun.cheng */public class JmsQueueSend {private JmsTemplate template;private Queue queue;/** * boolean pubSubDomain is false/true to create queue/topic *  * @param cf */public void setConnectionFactory(ConnectionFactory cf) {this.template = new JmsTemplate102(cf, false);}public void setQueue(Queue q) {this.queue = q;}public void simpleSend() {this.template.send(this.queue, new MessageCreator() {@Overridepublic Message createMessage(Session session) throws JMSException {return session.createTextMessage("holle spring JMS .");}});}}

接收消息

package com.ccl.jms;import javax.jms.JMSException;import javax.jms.Message;import javax.jms.MessageListener;import javax.jms.TextMessage;public class SimpleListener implements MessageListener {@Overridepublic void onMessage(Message msg) {if (msg instanceof TextMessage) {try {System.out.println(((TextMessage) msg).getText());} catch (JMSException e) {e.printStackTrace();}} else {System.out.println("IllegumentException");}}}

配置文件beans.xml

<!-- 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>

关于EJB3的Message Driven Bean:

ejb3 message driven bean