创建消息监听器:MarketingMdp不必实现MessageDrivenBean接口的世界是多么简单,见PPT12。MarketingMdp本身并不做些什么。它有一个实际处理消息的onMessage()方法。不过要先在Spring中配置一下:
<bean id="rantzMdp" class="com.roadrantz.marketing.MarketingMdp" />
EJB3.0MDB会使用@MessageDriven注释通知容器这是一个MDB。但是,在Spring中,我们会通过将其注入到一个消息监听容器来指示这个Bean是一个MDP。
包含消息监听器:消息监听器容器是一个用于查看JMS目标,等待消息到达的特殊Bean。一旦消息到达,它就可以获取到消息,并通过onMessage()方法将消息传递给一个MessageListener的实现。因为MarketingMdp类实现了MessageListener接口,所以消息监听器容器就准备完毕了。见PPT13. 顾名思义,SimpleMessageListenerContainer是最简单的消息监听器容器,可以按下面的方法在Spring中进行配置:
<bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="rantzDestination" />
<property name="messageListener" ref="rantzMdp" />
</bean>
对于messageListener属性,我们为其置入了对MDP实现的引用,这样,onMessage()方法将可以被用来接收消息。
使用事务性的MDP:如果收到的一个消息在事务中,则应该使用DefaultMessageListenerContainer:
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="rantzDestination" />
<property name="messageListener" ref="rantzMdp" />
<property name="transactionManager" ref="jmsTransactionManager" />
</bean> 如果事务性需求比较简单,JmsTransactionManager将按如下方法配置:
<bean id="jmsTransactionManager" class="org.springframework.jms.connection.JmsTransactionManager">
<property name="connectionFactory" ref="connectionFactory" />
</bean> 需要提醒的是,tansactionManger属性是可选的。如果不注入事务管理器,MDP就不是事务性的。
编写纯POJO MDP:如果消息监听器容器的messageListener属性被注入了MessageListener的实现,它就能够知道在消息到达时应该调用onMessage()方法。幸运的是,Spring提供了一个替代的MessageListenerAdapter。它是一个MessageListener,可以委派Bean和你选择的方法,见PPT14. 如果不将自己的MessageListener的实现注入到消息监听器容器中,你可以置入到MessageListenerAdapter中:
<bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="rantzDestination" />
<property name="messageListener" ref ="purePojoMdp" />
</bean> 因为配置了purePojoMdp Bean,所以它是一个MessageListenerAdapter:
<bean id="purePojoMdp" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<property name="delegate" ref="rantzMdp" />
<property name="defaultListenerMethod" value="processMotoristInfo" />
</bean> 默认情况下,MessageListenerAdapter在消息到达时会调用handleMessage()方法。但是,我们希望MarketingMdp Bean可以通过 processMotoristInfo()方法处理消息,因此将defaultListenerMethod设置为processMotoristInfo. 因为选择了一个特定的被调用方法,所以不需要实现MessageListener或onMessage()方法。因此 MarketingMdp现在将被简化为PPT15。尽管它是一个POJO,对MapMessage的依赖造成了MarketingMdp与JMS的不必要耦合,另外,MapMessage的getString方法还会抛出必须被处理的JMSException。理想情况下,MarketingMdp不应该依赖任何特定框架的类型。 当MessageListenerAdapter接收消息时,它会考虑消息的类型和defaultListenerMethod的值,并且尝试着查找用来调用的监听器方法符号。PPT16描述了MessageListenerAdapter是如何将JMS消息映射到监听器方法参数的。
转换MDP消息:在最新版中,processMotoristInfo()带有的是Map,并且在处理前需要将Map转换为SpammedMotorist。如果在消息到达时,能够直接给processMotoristInfo()方法传递可以处理的SpammedMotorist对象岂不是更好么?Spring消息转换器可以执行消息和特定域Java类型之间的相互转换工作。在PPT10中,已经有一个消息转换器。我们需要做的就是让MessageListenerAdapter能够感知这个消息转换器。MessageListenerAdapter的messageConverter属性可以完成这项工作:
<bean id="purePojoMdp" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<property name="delegate" ref="rantzMdp" />
<property name="defaultListenerMethod" value="processMotoristInfo" />
<propert name="messageConverter" ref="motoristConverter" />
</bean> 现在,可以编写最终版的MarketingMdp了。