Spring之JMS之接受消息

23.4.1 同步接受

JMS一般是異步處理,也有可能同步消費消息。重載receive(..)方法提供了這個功能。在同步接受期間,調用線程會一直阻塞直到消息可用。這會是很危險的操作,因爲調用線程可能隨機發生阻塞。receiveTimeout屬性指定了接收器在放棄等待一條消息前應該等待的時間。


23.4.2 異步接受---消息驅動 POJOs


需注意Spring通過使用@JmsListener也提供註解監聽端並提供一個開放底層程序化註冊端點。這是迄今爲止設置異步接收器最方便的方式。


與EJB中MDB相似,JMS 消息使用消息驅動的POJO即MDP作爲接收器。MDP的一個約束是必須要實現javax.jms.MessageListener接口。請注意這種情況,你的POJO在多線程下接受消息時,確保線程安全很重要。


下面是一個簡單的MDP的實現:

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

public class ExampleListener implements MessageListener {

    public void onMessage(Message message) {
        if (message instanceof TextMessage) {
            try {
                System.out.println(((TextMessage) message).getText());
            }
            catch (JMSException ex) {
                throw new RuntimeException(ex);
            }
        }
        else {
            throw new IllegalArgumentException("Message must be of type TextMessage");
        }
    }

}

只要實現了MessageListener接口,就該創建一個消息監聽容器了。


找出下面的例子中如何在Spring中定義和配置一個消息監聽容器的(這裏是DefaultMessageListenerContainer


<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="jmsexample.ExampleListener" />

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

23.4.3 SessionAwareMessageListener接口


SessionAwareMessageListener是Spring指定的接口,用於提供與JMS MessageListener接口相同的功能,但是也提供給消息處理方法,藉此從消息接受的對象中訪問JMS會話。

package org.springframework.jms.listener;

public interface SessionAwareMessageListener {

    void onMessage(Message message, Session session) throws JMSException;

}

你可以選擇性地使你的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類型的內容(其可以接受和處理的額)如何強制性類型轉換。


public interface MessageDelegate {

    void handleMessage(String message);

    void handleMessage(Map message);

    void handleMessage(byte[] message);

    void handleMessage(Serializable message);

}
public class DefaultMessageDelegate implements MessageDelegate {
    // implementation elided for clarity...
}

尤其是,上述的MessageDelegate接口的實現類(這裏是DefaultMessageDelegate類)沒有任何的JMS依賴。完全是一個POJO,通過以下配置轉換爲一個MDP。


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

下面是另一個MDP的例子,其能處理JMS TextMessage消息的接受。注意消息處理方法如何調用'receive'(MessageListenerAdapter類中消息處理方法名默認爲'handleMessage') ,但是它是可配置的(如下所示)。也注意到'receive(..)'方法如何強制類型轉化爲僅接受和響應JMSTextMessage消息。


public interface TextMessageDelegate {

    void receive(TextMessage message);

}
public class DefaultTextMessageDelegate implements TextMessageDelegate {
    // implementation elided for clarity...
}

相應的MessageListenerAdapter配置如下:

<bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
    <constructor-arg>
        <bean class="jmsexample.DefaultTextMessageDelegate"/>
    </constructor-arg>
    <property name="defaultListenerMethod" value="receive"/>
    <!-- we don't want automatic message context extraction -->
    <property name="messageConverter">
        <null/>
    </property>
</bean>


請注意上述的'messageListener'接受了一個JMS 消息,而不是TextMessage類型,將拋出一個IllegalStateException異常。MessageListenerAdapter類的另一個功能是自動發送回一個響應消息,如果處理器方法返回一個有效值的話。看下面的接口和類:


public interface ResponsiveTextMessageDelegate {

    // notice the return type...
    String receive(TextMessage message);

}
public class DefaultResponsiveTextMessageDelegate implements ResponsiveTextMessageDelegate {
    // implementation elided for clarity...
}

如果上述的DefaultResponsiveTextMessageDelegateMessageListenerAdapter聯合使用,從執行的'receive(..)方法中返回一個非空值,將轉換爲一個TextMessage。結果TextMessage將在稍後發送到Destination(如果存在),其在JMS 的初始Message的Reply-To屬性中定義,或者MessageListenerAdapter定義的默認的Destination(如果已經配置了);如果沒有發現Destination,則拋出異常InvalidDestinationException(請注意這個異常將不被容忍,並將傳送到調用回調)。


23..4.5 事物內處理消息


在一個事物中調用消息監聽器僅僅需要重新配置監聽容器。

定位資源事物可以通過監聽容器定義的sessionTransacted標示激活。每個消息監聽調用將在一個活性的JMS事物中執行,並在監聽執行失敗時,消息接受會回滾。發送一個響應消息(藉助SessionAwareMessageListener)將是相同本地事物一部分,但是另外的資源操作(比如數據庫訪問)將分開操作。這通常需要在監聽實現中進行多重的消息查找,囊括了這種情況,提交了數據庫處理但是消息處理提交失敗。


<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <property name="connectionFactory" ref="connectionFactory"/>
    <property name="destination" ref="destination"/>
    <property name="messageListener" ref="messageListener"/>
    <property name="sessionTransacted" value="true"/>
</bean>

對於外部管理事物中的多人蔘與情況,你需要配置一個事物管理器並使用一個監聽容器,其支持外部事物管理:一般的是DefaultMessageListenerContainer


對於XA事物參與配置的消息監聽容器,你將想要配置一個JtaTransactionManager(默認地,委託給Java EE服務器的事物子系統)。注意到底層的JMS ConnectionFactory需要是XA-capable並註冊你的JTA 事物協調器。


<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

之後 你只需要將其添加到你早期的容器配置中。容器就負責剩餘的工作了:

<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <property name="connectionFactory" ref="connectionFactory"/>
    <property name="destination" ref="destination"/>
    <property name="messageListener" ref="messageListener"/>
    <property name="transactionManager" ref="transactionManager"/>
</bean>




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章