1. 程式人生 > >Spring整合JMS——基於ActiveMQ實現(附原始碼)

Spring整合JMS——基於ActiveMQ實現(附原始碼)

宣告:本文轉載地址:http://elim.iteye.com/blog/1893038,如不能轉載,請聯絡博主。
此原始碼非彼原始碼,如需下載原文原始碼,請訪問原文連結。
此原始碼是博主經過改造測試的,可以配合此文章閱讀。地址:去下載

JMS簡介

JMS的全稱是Java Message Service,即Java訊息服務。它主要用於在生產者和消費者之間進行訊息傳遞,生產者負責產生訊息,而消費者負責接收訊息。把它應用到實際的業務需求中的話我們可以在特定的時候利用生產者生成一訊息,並進行傳送,對應的消費者在接收到對應的訊息後去完成對應的業務邏輯。對於訊息的傳遞有兩種型別,一種是點對點的,即一個生產者和一個消費者一一對應;另一種是釋出/訂閱模式,即一個生產者產生訊息並進行傳送後,可以由多個消費者進行接收。

Spring整合JMS

對JMS做了一個簡要介紹之後,接下來就講一下Spring整合JMS的具體過程。JMS只是一個標準,真正在使用它的時候我們需要有它的具體實現,這裡我們就使用Apache的activeMQ來作為它的實現。所使用的依賴利用Maven來進行管理,具體依賴如下:

  <properties>
    <spring-version>4.1.0.RELEASE</spring-version>
  </properties>
<dependencies>  
        <dependency>  
            <groupId
>
junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId
>
spring-core</artifactId> <version>${spring-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring-version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.7.4</version> </dependency> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-core</artifactId> <version>5.7.0</version> </dependency> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-pool</artifactId> <version>5.14.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>4.3.2.RELEASE</version> </dependency> </dependencies>

activeMQ準備

既然是使用的apache的activeMQ作為JMS的實現,那麼首先我們應該到apache官網上下載activeMQ(http://activemq.apache.org/download.html),進行解壓後執行其bin目錄下面的activemq.bat檔案啟動activeMQ。

配置ConnectionFactory

ConnectionFactory是用於產生到JMS伺服器的連結的,Spring為我們提供了多個ConnectionFactory,有SingleConnectionFactory和CachingConnectionFactory。SingleConnectionFactory對於建立JMS伺服器連結的請求會一直返回同一個連結,並且會忽略Connection的close方法呼叫。CachingConnectionFactory繼承了SingleConnectionFactory,所以它擁有SingleConnectionFactory的所有功能,同時它還新增了快取功能,它可以快取Session、MessageProducer和MessageConsumer。這裡我們使用SingleConnectionFactory來作為示例。

<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory"/>  

這樣就定義好產生JMS伺服器連結的ConnectionFactory了嗎?答案是非也。Spring提供的ConnectionFactory只是Spring用於管理ConnectionFactory的,真正產生到JMS伺服器連結的ConnectionFactory還得是由JMS服務廠商提供,並且需要把它注入到Spring提供的ConnectionFactory中。我們這裡使用的是ActiveMQ實現的JMS,所以在我們這裡真正的可以產生Connection的就應該是由ActiveMQ提供的ConnectionFactory。所以定義一個ConnectionFactory的完整程式碼應該如下所示:

<!-- 真正可以產生Connection的ConnectionFactory,由對應的 JMS服務廠商提供-->  
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">  
    <property name="brokerURL" value="tcp://localhost:61616"/>  
</bean>  

<!-- Spring用於管理真正的ConnectionFactory的ConnectionFactory -->  
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">  
    <!-- 目標ConnectionFactory對應真實的可以產生JMS Connection的ConnectionFactory -->  
    <property name="targetConnectionFactory" ref="targetConnectionFactory"/>  
</bean>  

ActiveMQ為我們提供了一個PooledConnectionFactory,通過往裡面注入一個ActiveMQConnectionFactory可以用來將Connection、Session和MessageProducer池化,這樣可以大大的減少我們的資源消耗。當使用PooledConnectionFactory時,我們在定義一個ConnectionFactory時應該是如下定義:

<!-- 真正可以產生Connection的ConnectionFactory,由對應的 JMS服務廠商提供-->  
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">  
    <property name="brokerURL" value="tcp://localhost:61616"/>  
</bean>  

<bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">  
    <property name="connectionFactory" ref="targetConnectionFactory"/>  
    <property name="maxConnections" value="10"/>  
</bean>  

<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">  
    <property name="targetConnectionFactory" ref="pooledConnectionFactory"/>  
</bean>  

配置生產者

配置好ConnectionFactory之後我們就需要配置生產者。生產者負責產生訊息併發送到JMS伺服器,這通常對應的是我們的一個業務邏輯服務實現類。但是我們的服務實現類是怎麼進行訊息的傳送的呢?這通常是利用Spring為我們提供的JmsTemplate類來實現的,所以配置生產者其實最核心的就是配置進行訊息傳送的JmsTemplate。對於訊息傳送者而言,它在傳送訊息的時候要知道自己該往哪裡發,為此,我們在定義JmsTemplate的時候需要往裡面注入一個Spring提供的ConnectionFactory物件。

<!-- Spring提供的JMS工具類,它可以進行訊息傳送、接收等 -->  
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">  
    <!-- 這個connectionFactory對應的是我們定義的Spring提供的那個ConnectionFactory物件 -->  
    <property name="connectionFactory" ref="connectionFactory"/>  
</bean>  

在真正利用JmsTemplate進行訊息傳送的時候,我們需要知道訊息傳送的目的地,即destination。在Jms中有一個用來表示目的地的Destination介面,它裡面沒有任何方法定義,只是用來做一個標識而已。當我們在使用JmsTemplate進行訊息傳送時沒有指定destination的時候將使用預設的Destination。預設Destination可以通過在定義jmsTemplate bean物件時通過屬性defaultDestination或defaultDestinationName來進行注入,defaultDestinationName對應的就是一個普通字串。在ActiveMQ中實現了兩種型別的Destination,一個是點對點的ActiveMQQueue,另一個就是支援訂閱/釋出模式的ActiveMQTopic。在定義這兩種型別的Destination時我們都可以通過一個name屬性來進行構造,如:

<!--這個是佇列目的地,點對點的-->  
<bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">  
    <constructor-arg>  
        <value>queue</value>  
    </constructor-arg>  
</bean>  
<!--這個是主題目的地,一對多的-->  
<bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">  
    <constructor-arg value="topic"/>  
</bean>  

假設我們定義了一個ProducerService,裡面有一個向Destination傳送純文字訊息的方法sendMessage,那麼我們的程式碼就大概是這個樣子:

package com.tiantian.springintejms.service.impl;  

import javax.annotation.Resource;  
import javax.jms.Destination;  
import javax.jms.JMSException;  
import javax.jms.Message;  
import javax.jms.Session;  

import org.springframework.jms.core.JmsTemplate;  
import org.springframework.jms.core.MessageCreator;  
import org.springframework.stereotype.Component;  

import com.tiantian.springintejms.service.ProducerService;  

@Component  
public class ProducerServiceImpl implements ProducerService {  

    private JmsTemplate jmsTemplate;  

    public void sendMessage(Destination destination, final String message) {  
        System.out.println("---------------生產者傳送訊息-----------------");  
        System.out.println("---------------生產者發了一個訊息:" + message);  
        jmsTemplate.send(destination, new MessageCreator() {  
            public Message createMessage(Session session) throws JMSException {  
                return session.createTextMessage(message);  
            }  
        });  
    }   

    public JmsTemplate getJmsTemplate() {  
        returnjmsTemplate;  
    }   

    @Resource  
    public void setJmsTemplate(JmsTemplate jmsTemplate) {  
        this.jmsTemplate = jmsTemplate;  
    }  

}  

我們可以看到在sendMessage方法體裡面我們是通過jmsTemplate來發送訊息到對應的Destination的。到此,我們生成一個簡單的文字訊息並把它傳送到指定目的地Destination的生產者就配置好了。

配置消費者

生產者往指定目的地Destination傳送訊息後,接下來就是消費者對指定目的地的訊息進行消費了。那麼消費者是如何知道有生產者傳送訊息到指定目的地Destination了呢?這是通過Spring為我們封裝的訊息監聽容器MessageListenerContainer實現的,它負責接收資訊,並把接收到的資訊分發給真正的MessageListener進行處理。每個消費者對應每個目的地都需要有對應的MessageListenerContainer。對於訊息監聽容器而言,除了要知道監聽哪個目的地之外,還需要知道到哪裡去監聽,也就是說它還需要知道去監聽哪個JMS伺服器,這是通過在配置MessageConnectionFactory的時候往裡面注入一個ConnectionFactory來實現的。所以我們在配置一個MessageListenerContainer的時候有三個屬性必須指定,一個是表示從哪裡監聽的ConnectionFactory;一個是表示監聽什麼的Destination;一個是接收到訊息以後進行訊息處理的MessageListener。Spring一共為我們提供了兩種型別的MessageListenerContainer,SimpleMessageListenerContainer和DefaultMessageListenerContainer。
SimpleMessageListenerContainer會在一開始的時候就建立一個會話session和消費者Consumer,並且會使用標準的JMS MessageConsumer.setMessageListener()方法註冊監聽器讓JMS提供者呼叫監聽器的回撥函式。它不會動態的適應執行時需要和參與外部的事務管理。相容性方面,它非常接近於獨立的JMS規範,但一般不相容Java EE的JMS限制。
大多數情況下我們還是使用的DefaultMessageListenerContainer,跟SimpleMessageListenerContainer相比,DefaultMessageListenerContainer會動態的適應執行時需要,並且能夠參與外部的事務管理。它很好的平衡了對JMS提供者要求低、先進功能如事務參與和相容Java EE環境。

定義處理訊息的MessageListener

要定義處理訊息的MessageListener我們只需要實現JMS規範中的MessageListener介面就可以了。MessageListener介面中只有一個方法onMessage方法,當接收到訊息的時候會自動呼叫該方法。

package com.tiantian.springintejms.listener;  

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

public class ConsumerMessageListener implements MessageListener {  

    public void onMessage(Message message) {  
        //這裡我們知道生產者傳送的就是一個純文字訊息,所以這裡可以直接進行強制轉換  
        TextMessage textMsg = (TextMessage) message;  
        System.out.println("接收到一個純文字訊息。");  
        try {  
            System.out.println("訊息內容是:" + textMsg.getText());  
        } catch (JMSException e) {  
            e.printStackTrace();  
        }  
    }  

}  

有了MessageListener之後我們就可以在Spring的配置檔案中配置一個訊息監聽容器了。

<!--這個是佇列目的地-->  
<bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">  
    <constructor-arg>  
        <value>queue</value>  
    </constructor-arg>  
</bean>  
<!-- 訊息監聽器 -->  
<bean id="consumerMessageListener" class="com.tiantian.springintejms.listener.ConsumerMessageListener"/>      

<!-- 訊息監聽容器 -->  
<bean id="jmsContainer"        class="org.springframework.jms.listener.DefaultMessageListenerContainer">  
    <property name="connectionFactory" ref="connectionFactory" />  
    <property name="destination" ref="queueDestination" />  
    <property name="messageListener" ref="consumerMessageListener" />  
</bean>  

我們可以看到我們定義了一個名叫queue的ActiveMQQueue目的地,我們的監聽器就是監聽了傳送到這個目的地的訊息。
至此我們的生成者和消費者都配置完成了,這也就意味著我們的整合已經完成了。這個時候完整的Spring的配置檔案應該是這樣的:

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:jms="http://www.springframework.org/schema/jms"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans  
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
     http://www.springframework.org/schema/context  
     http://www.springframework.org/schema/context/spring-context-3.0.xsd  
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
    http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd">  

    <context:component-scan base-package="com.tiantian" />  

    <!-- Spring提供的JMS工具類,它可以進行訊息傳送、接收等 -->  
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">  
        <!-- 這個connectionFactory對應的是我們定義的Spring提供的那個ConnectionFactory物件 -->  
        <property name="connectionFactory" ref="connectionFactory"/>  
    </bean>  

    <!-- 真正可以產生Connection的ConnectionFactory,由對應的 JMS服務廠商提供-->  
    <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">  
        <property name="brokerURL" value="tcp://localhost:61616"/>  
    </bean>  

    <!-- Spring用於管理真正的ConnectionFactory的ConnectionFactory -->  
    <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">  
        <!-- 目標ConnectionFactory對應真實的可以產生JMS Connection的ConnectionFactory -->  
        <property name="targetConnectionFactory" ref="targetConnectionFactory"/>  
    </bean>  

    <!--這個是佇列目的地-->  
    <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">  
        <constructor-arg>  
            <value>queue</value>  
        </constructor-arg>  
    </bean>  
    <!-- 訊息監聽器 -->  
    <bean id="consumerMessageListener" class="com.tiantian.springintejms.listener.ConsumerMessageListener"/>  
    <!-- 訊息監聽容器 -->  
    <bean id="jmsContainer"  
        class="org.springframework.jms.listener.DefaultMessageListenerContainer">  
        <property name="connectionFactory" ref="connectionFactory" />  
        <property name="destination" ref="queueDestination" />  
        <property name="messageListener" ref="consumerMessageListener" />  
    </bean>  
</beans>  

接著我們來測試一下,看看我們的整合是否真的成功了,測試程式碼如下:

package com.tiantian.springintejms.test;  

import javax.jms.Destination;  

import org.junit.Test;  
import org.junit.runner.RunWith;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.beans.factory.annotation.Qualifier;  
import org.springframework.test.context.ContextConfiguration;  
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  
import com.tiantian.springintejms.service.ProducerService;  

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration("/applicationContext.xml")  
public class ProducerConsumerTest {  

    @Autowired  
    private ProducerService producerService;  
    @Autowired  
    @Qualifier("queueDestination")  
    private Destination destination;  

    @Test  
    public void testSend() {  
        for (int i=0; i<2; i++) {  
            producerService.sendMessage(destination, "你好,生產者!這是訊息:" + (i+1));  
        }  
    }  

}  

在上面的測試程式碼中我們利用生產者傳送了兩個訊息,正常來說,消費者應該可以接收到這兩個訊息。執行測試程式碼後控制檯輸出如下:
這裡寫圖片描述
看,控制檯已經進行了正確的輸出,這說明我們的整合確實是已經成功了。

訊息監聽器MessageListener

在Spring整合JMS的應用中我們在定義訊息監聽器的時候一共可以定義三種類型的訊息監聽器,分別是MessageListener、SessionAwareMessageListener和MessageListenerAdapter。下面就分別來介紹一下這幾種型別的區別。

MessageListener

MessageListener是最原始的訊息監聽器,它是JMS規範中定義的一個介面。其中定義了一個用於處理接收到的訊息的onMessage方法,該方法只接收一個Message引數。我們前面在講配置消費者的時候用的訊息監聽器就是MessageListener,程式碼如下:

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

public class ConsumerMessageListener implements MessageListener {  

    public void onMessage(Message message) {  
        //這裡我們知道生產者傳送的就是一個純文字訊息,所以這裡可以直接進行強制轉換,或者直接把onMessage方法的引數改成Message的子類TextMessage  
        TextMessage textMsg = (TextMessage) message;  
        System.out.println("接收到一個純文字訊息。");  
        try {  
            System.out.println("訊息內容是:" + textMsg.getText());  
        } catch (JMSException e) {  
            e.printStackTrace();  
        }  
    }  

}  

SessionAwareMessageListener

SessionAwareMessageListener是Spring為我們提供的,它不是標準的JMS MessageListener。MessageListener的設計只是純粹用來接收訊息的,假如我們在使用MessageListener處理接收到的訊息時我們需要傳送一個訊息通知對方我們已經收到這個訊息了,那麼這個時候我們就需要在程式碼裡面去重新獲取一個Connection或Session。SessionAwareMessageListener的設計就是為了方便我們在接收到訊息後傳送一個回覆的訊息,它同樣為我們提供了一個處理接收到的訊息的onMessage方法,但是這個方法可以同時接收兩個引數,一個是表示當前接收到的訊息Message,另一個就是可以用來發送訊息的Session物件。先來看一段程式碼:

package com.tiantian.springintejms.listener;  

import javax.jms.Destination;  
import javax.jms.JMSException;  
import javax.jms.Message;  
import javax.jms.MessageProducer;  
import javax.jms.Session;  
import javax.jms.TextMessage;  

import org.springframework.jms.listener.SessionAwareMessageListener;  

public class ConsumerSessionAwareMessageListener implements  
        SessionAwareMessageListener<TextMessage> {  

    private Destination destination;  

    public void onMessage(TextMessage message, Session session) throws JMSException {  
        System.out.println("收到一條訊息");  
        System.out.println("訊息內容是:" + message.getText());  
        MessageProducer producer = session.createProducer(destination);  
        Message textMessage = session.createTextMessage("ConsumerSessionAwareMessageListener。。。");  
        producer.send(textMessage);  
    }  

    public Destination getDestination() {  
        returndestination;  
    }  

    public void setDestination(Destination destination) {  
        this.destination = destination;  
    }  

}  

在上面程式碼中我們定義了一個SessionAwareMessageListener,在這個Listener中我們在接收到了一個訊息之後,利用對應的Session建立了一個到destination的生產者和對應的訊息,然後利用建立好的生產者傳送對應的訊息。
接著我們在Spring的配置檔案中配置該訊息監聽器將處理來自一個叫sessionAwareQueue的目的地的訊息,並且往該MessageListener中通過set方法注入其屬性destination的值為queueDestination。這樣當我們的SessionAwareMessageListener接收到訊息之後就會往queueDestination傳送一個訊息。

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:jms="http://www.springframework.org/schema/jms"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans  
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
     http://www.springframework.org/schema/context  
     http://www.springframework.org/schema/context/spring-context-3.0.xsd  
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
    http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd">  

    <context:component-scan base-package="com.tiantian" />   
    <!-- Spring提供的JMS工具類,它可以進行訊息傳送、接收等 -->  
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">  
        <!-- 這個connectionFactory對應的是我們定義的Spring提供的那個ConnectionFactory物件 -->  
        <property name="connectionFactory" ref="connectionFactory"/>  
    </bean>  

    <!-- 真正可以產生Connection的ConnectionFactory,由對應的 JMS服務廠商提供-->  
    <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">  
        <property name="brokerURL" value="tcp://localhost:61616"/>  
    </bean>  

    <!-- Spring用於管理真正的ConnectionFactory的ConnectionFactory -->  
    <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">  
        <!-- 目標ConnectionFactory對應真實的可以產生JMS Connection的ConnectionFactory -->  
        <property name="targetConnectionFactory" ref="targetConnectionFactory"/>  
    </bean>  

    <!--這個是佇列目的地-->  
    <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">  
        <constructor-arg>  
            <value>queue</value>  
        </constructor-arg>  
    </bean>  
    <!--這個是sessionAwareQueue目的地-->  
    <bean id="sessionAwareQueue" class="org.apache.activemq.command.ActiveMQQueue">  
        <constructor-arg>  
            <value>sessionAwareQueue</value>  
        </constructor-arg>  
    </bean>  
    <!-- 訊息監聽器 -->  
    <bean id="consumerMessageListener" class="com.tiantian.springintejms.listener.ConsumerMessageListener"/>  
    <!-- 可以獲取session的MessageListener -->  
    <bean id="consumerSessionAwareMessageListener" class="com.tiantian.springintejms.listener.ConsumerSessionAwareMessageListener">  
        <property name="destination" ref="queueDestination"/>  
    </bean>  
    <!-- 訊息監聽容器 -->  
    <bean id="jmsContainer"        class="org.springframework.jms.listener.DefaultMessageListenerContainer">  
        <property name="connectionFactory" ref="connectionFactory" />  
        <property name="destination" ref="queueDestination" />  
        <property name="messageListener" ref="consumerMessageListener" />  
    </bean>  

    <bean id="sessionAwareListenerContainer"  
        class="org.springframework.jms.listener.DefaultMessageListenerContainer">  
        <property name="connectionFactory" ref="connectionFactory" />  
        <property name="destination" ref="sessionAwareQueue" />  
        <property name="messageListener" ref="consumerSessionAwareMessageListener" />  
    </bean>  
</beans>  

接著我們來做一個測試,測試程式碼如下:

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration("/applicationContext.xml")  
public class ProducerConsumerTest {  

    @Autowired  
    private ProducerService producerService;  
    @Autowired  
    @Qualifier("sessionAwareQueue")  
    private Destination sessionAwareQueue;  

    @Test  
    public void testSessionAwareMessageListener() {  
        producerService.sendMessage(sessionAwareQueue, "測試SessionAwareMessageListener");  
    }  

}  

在上述測試程式碼中,我們通過前面定義好的生產者往我們定義好的SessionAwareMessageListener監聽的sessionAwareQueue傳送了一個訊息。程式執行之後控制檯輸出如下:
這裡寫圖片描述
這說明我們已經成功的往sessionAwareQueue傳送了一條純文字訊息,訊息會被ConsumerSessionAwareMessageListener的onMessage方法進行處理,在onMessage方法中ConsumerSessionAwareMessageListener就是簡單的把接收到的純文字資訊的內容打印出來了,之後再往queueDestination傳送了一個純文字訊息,訊息內容是“ConsumerSessionAwareMessageListener…”,該訊息隨後就被ConsumerMessageListener處理了,根據我們的定義,在ConsumerMessageListener中也只是簡單的列印了一下接收到的訊息內容。

MessageListenerAdapter

MessageListenerAdapter類實現了MessageListener介面和SessionAwareMessageListener介面,它的主要作用是將接收到的訊息進行型別轉換,然後通過反射的形式把它交給一個普通的Java類進行處理。
MessageListenerAdapter會把接收到的訊息做如下轉換:
1. TextMessage轉換為String物件;
2. BytesMessage轉換為byte陣列;
3. MapMessage轉換為Map物件;
4. ObjectMessage轉換為對應的Serializable物件。
既然前面說了MessageListenerAdapter會把接收到的訊息做一個型別轉換,然後利用反射把它交給真正的目標處理器——一個普通的Java類進行處理(如果真正的目標處理器是一個MessageListener或者是一個SessionAwareMessageListener,那麼Spring將直接使用接收到的Message物件作為引數呼叫它們的onMessage方法,而不會再利用反射去進行呼叫),那麼我們在定義一個MessageListenerAdapter的時候就需要為它指定這樣一個目標類。這個目標類我們可以通過MessageListenerAdapter的構造方法引數指定,如:

<!-- 訊息監聽介面卡 -->  
    <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">  
        <constructor-arg>  
            <bean class="com.tiantian.springintejms.listener.ConsumerListener"/>  
        </constructor-arg>  
    </bean>  

也可以通過它的delegate屬性來指定,如:

<!-- 訊息監聽介面卡 -->  
    <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">  
        <property name="delegate">  
            <bean class="com.tiantian.springintejms.listener.ConsumerListener"/>  
        </property>  
        <property name="defaultListenerMethod" value="receiveMessage"/>  
    </bean>  

前面說了如果我們指定的這個目標處理器是一個MessageListener或者是一個SessionAwareMessageListener的時候Spring將直接利用接收到的Message物件作為方法引數呼叫它們的onMessage方法。但是如果指定的目標處理器是一個普通的Java類時Spring將利用Message進行了型別轉換之後的物件作為引數通過反射去呼叫真正的目標處理器的處理方法,那麼Spring是如何知道該呼叫哪個方法呢?這是通過MessageListenerAdapter的defaultListenerMethod屬性來決定的,當我們沒有指定該屬性時,Spring會預設呼叫目標處理器的handleMessage方法。
接下來我們來看一個示例,假設我們有一個普通的Java類ConsumerListener,其對應有兩個方法,handleMessage和receiveMessage,其程式碼如下:

package com.tiantian.springintejms.listener;  

public class ConsumerListener {  

    public void handleMessage(String message) {  
        System.out.println("ConsumerListener通過handleMessage接收到一個純文字訊息,訊息內容是:" + message);  
    }  

    public void receiveMessage(String message) {  
        System.out.println("ConsumerListener通過receiveMessage接收到一個純文字訊息,訊息內容是:" + message);  
    }  

}  

假設我們要把它作為一個訊息監聽器來監聽傳送到adapterQueue的訊息,這個時候我們就可以定義一個對應的MessageListenerAdapter來把它當做一個MessageListener使用。

<!-- 訊息監聽介面卡 -->  
<bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">  
    <property name="delegate">  
        <bean class="com.tiantian.springintejms.listener.ConsumerListener"/>  
    </property>  
    <property name="defaultListenerMethod" value="receiveMessage"/>  
</bean>  

當然,有了MessageListener之後我們還需要配置其對應的MessageListenerContainer,這裡配置如下:

    <!-- 訊息監聽介面卡對應的監聽容器 -->  
    <bean id="messageListenerAdapterContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">  
        <property name="connectionFactory" ref="connectionFactory"/>  
        <property name="destination" ref="adapterQueue"/>  
        <property name="messageListener" ref="messageListenerAdapter"/><!-- 使用MessageListenerAdapter來作為訊息監聽器 -->  
    </bean>  

<!-- 用於測試訊息監聽介面卡的佇列目的地 -->  
    <bean id="adapterQueue" class="org.apache.activemq.command.ActiveMQQueue"&g