創(chuàng)建消息監(jiān)聽器:MarketingMdp不必實(shí)現(xiàn)MessageDrivenBean接口的世界是多么簡(jiǎn)單,見(jiàn)PPT12。MarketingMdp本身并不做些什么。它有一個(gè)實(shí)際處理消息的onMessage()方法。不過(guò)要先在Spring中配置一下:
<bean id="rantzMdp" class="com.roadrantz.marketing.MarketingMdp" />
EJB3.0MDB會(huì)使用@MessageDriven注釋通知容器這是一個(gè)MDB。但是,在Spring中,我們會(huì)通過(guò)將其注入到一個(gè)消息監(jiān)聽容器來(lái)指示這個(gè)Bean是一個(gè)MDP。
包含消息監(jiān)聽器:消息監(jiān)聽器容器是一個(gè)用于查看JMS目標(biāo),等待消息到達(dá)的特殊Bean。一旦消息到達(dá),它就可以獲取到消息,并通過(guò)onMessage()方法將消息傳遞給一個(gè)MessageListener的實(shí)現(xiàn)。因?yàn)镸arketingMdp類實(shí)現(xiàn)了MessageListener接口,所以消息監(jiān)聽器容器就準(zhǔn)備完畢了。見(jiàn)PPT13. 顧名思義,SimpleMessageListenerContainer是最簡(jiǎn)單的消息監(jiān)聽器容器,可以按下面的方法在Spring中進(jìn)行配置:
<bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="rantzDestination" />
<property name="messageListener" ref="rantzMdp" />
</bean>
對(duì)于messageListener屬性,我們?yōu)槠渲萌肓藢?duì)MDP實(shí)現(xiàn)的引用,這樣,onMessage()方法將可以被用來(lái)接收消息。
使用事務(wù)性的MDP:如果收到的一個(gè)消息在事務(wù)中,則應(yīng)該使用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> 如果事務(wù)性需求比較簡(jiǎn)單,JmsTransactionManager將按如下方法配置:
<bean id="jmsTransactionManager" class="org.springframework.jms.connection.JmsTransactionManager">
<property name="connectionFactory" ref="connectionFactory" />
</bean> 需要提醒的是,tansactionManger屬性是可選的。如果不注入事務(wù)管理器,MDP就不是事務(wù)性的。
編寫純POJO MDP:如果消息監(jiān)聽器容器的messageListener屬性被注入了MessageListener的實(shí)現(xiàn),它就能夠知道在消息到達(dá)時(shí)應(yīng)該調(diào)用onMessage()方法。幸運(yùn)的是,Spring提供了一個(gè)替代的MessageListenerAdapter。它是一個(gè)MessageListener,可以委派Bean和你選擇的方法,見(jiàn)PPT14. 如果不將自己的MessageListener的實(shí)現(xiàn)注入到消息監(jiān)聽器容器中,你可以置入到MessageListenerAdapter中:
<bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="rantzDestination" />
<property name="messageListener" ref ="purePojoMdp" />
</bean> 因?yàn)榕渲昧藀urePojoMdp Bean,所以它是一個(gè)MessageListenerAdapter:
<bean id="purePojoMdp" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<property name="delegate" ref="rantzMdp" />
<property name="defaultListenerMethod" value="processMotoristInfo" />
</bean> 默認(rèn)情況下,MessageListenerAdapter在消息到達(dá)時(shí)會(huì)調(diào)用handleMessage()方法。但是,我們希望MarketingMdp Bean可以通過(guò) processMotoristInfo()方法處理消息,因此將defaultListenerMethod設(shè)置為processMotoristInfo. 因?yàn)檫x擇了一個(gè)特定的被調(diào)用方法,所以不需要實(shí)現(xiàn)MessageListener或onMessage()方法。因此 MarketingMdp現(xiàn)在將被簡(jiǎn)化為PPT15。盡管它是一個(gè)POJO,對(duì)MapMessage的依賴造成了MarketingMdp與JMS的不必要耦合,另外,MapMessage的getString方法還會(huì)拋出必須被處理的JMSException。理想情況下,MarketingMdp不應(yīng)該依賴任何特定框架的類型。 當(dāng)MessageListenerAdapter接收消息時(shí),它會(huì)考慮消息的類型和defaultListenerMethod的值,并且嘗試著查找用來(lái)調(diào)用的監(jiān)聽器方法符號(hào)。PPT16描述了MessageListenerAdapter是如何將JMS消息映射到監(jiān)聽器方法參數(shù)的。
轉(zhuǎn)換MDP消息:在最新版中,processMotoristInfo()帶有的是Map,并且在處理前需要將Map轉(zhuǎn)換為SpammedMotorist。如果在消息到達(dá)時(shí),能夠直接給processMotoristInfo()方法傳遞可以處理的SpammedMotorist對(duì)象豈不是更好么?Spring消息轉(zhuǎn)換器可以執(zhí)行消息和特定域Java類型之間的相互轉(zhuǎn)換工作。在PPT10中,已經(jīng)有一個(gè)消息轉(zhuǎn)換器。我們需要做的就是讓MessageListenerAdapter能夠感知這個(gè)消息轉(zhuǎn)換器。MessageListenerAdapter的messageConverter屬性可以完成這項(xiàng)工作:
<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> 現(xiàn)在,可以編寫最終版的MarketingMdp了。