創建消息監聽器: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了。