領先一步
VMware 提供培訓和認證,以加速你的進度。
了解更多隨著 Spring 1.1 的發布,Spring 社群首次體驗了 JMS 支援。 此支援包括例外轉換、訊息轉換和一個範本類別,非常像 JdbcTemplate。 此支援還處理了 JMS 1.0.2 和 1.1 規範之間的網域統一。 此支援的核心是 JmsTemplate 類別及其 JMS 1.0.2 對應物 JmsTemplate102。
與使用原始 JMS API 進行企業訊息傳遞相比,此支援是一項很大的改進。 然而,它確實有一個缺點;JmsTemplate 僅支援使用 JmsTemplate.receive() 方法同步接收訊息。 這種行為對許多人來說效果很好,但絕大多數使用者最終都推出了自己的非同步消費者實作。 簡而言之,他們想要 EJB 2 稱為 訊息驅動 Bean 的東西。
但使用者不再需要如此。 隨著 2.0M1 的發布和稍後的最終 2.0 版本,已新增對 JMS 訊息非同步接收的本機支援。 JmsTemplate 仍用於傳送訊息,就像一直以來一樣,但現在已加入 AbstractMessageListenerContainer 的子類別,例如 DefaultMessageListenerContainer、SimpleMessageListenerContainer 和 ServerSessionMessageListener。
讓我們看看如何使用這些 MessageListenerContainer。 第一步是建立一個可以接收訊息的類別。 為此,必須建立一個實作 MessageListener 介面的類別。
package jmsexample;
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 e) {
throw new RuntimeException(e);
}
} else {
throw new IllegalArgumentException(
"Message must be of type TestMessage");
}
}
}
一旦你有了這個,你需要一個訊息生產者。 此程式碼與 Spring 2.0 之前相同,因此如果你已經有這樣做的程式碼,則不應進行任何變更。
package jmsexample;
import org.springframework.jms.core.JmsTemplate;
public class ExampleProducer {
private JmsTemplate jmsTemplate;
public ExampleProducer(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public void sendMessage() {
jmsTemplate.convertAndSend("Example Message");
}
}
接下來,你需要配置你的 context 以建立一個 MessageListenerContainer,將訊息路由到此 bean。 你會注意到我在這個範例中使用 ActiveMQ 實作類別。 這只是許多 JMS 實作之一,而且恰好是我最熟悉的一個。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="messageListener" class="jmsexample.ExampleListener" />
<bean id="messageProducer" class="jmsexample.ExampleProducer">
<constructor-arg ref="jmsTemplate" />
</bean>
<bean id="jmsTemplate"
class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="defaultDestination" ref="destination" />
</bean>
<bean id="destination" class="org.activemq.message.ActiveMQQueue">
<constructor-arg value="jmsExample" />
</bean>
<bean id="listenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="destination" />
<property name="messageListener" ref="messageListener" />
</bean>
<bean id="connectionFactory"
class="org.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://127.0.0.1:61616" />
</bean>
</beans>
我現在要跳過它,但顯然你需要啟動一個 MQ,以及一個引導你的 context 的 main 方法。 我已經新增了這個範例的專案歸檔,以便你在需要時查看其餘程式碼。
最後,你只需要運行你的應用程式並查看輸出。
Example Message
需要注意的一件事是,到目前為止,我們一直在處理具有單個消費者執行緒的非同步接收。 可以使用 MessageListenerContainer 的 concurrent consumers 屬性將你的消費者進行多執行緒處理(請記住,你仍然必須使它們無狀態或執行緒安全)。
<bean id="listenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="concurrentConsumers" value="5" />
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="destination" />
<property name="messageListener" ref="messageListener" />
</bean>
我想指出的一件事(從我自己的痛苦經歷中)是確保你不要將 concurrent consumers 與 Topic 一起使用。 請記住,在 JMS topic 中,所有訊息都會傳遞到 topic 上的所有消費者。 這意味著如果你在 topic 上有 concurrent consumers,他們都會收到相同的訊息; 通常是你想要避免的事情。 但是,如果你使用的是佇列,顯然這會以循環方式將每個新訊息分派給消費者。
所以,你明白了。 它不是很花哨,而且可能與你曾經編寫過的程式碼非常相似,但現在你所要做的就是使用它,你不需要維護它。 我還要說這只是冰山一角。 MessageListenerContainer 具有參與交易的能力,使用自訂執行緒池(例如應用程式伺服器提供的執行緒池)與新的 Spring TaskExecutor 抽象,甚至向消費者公開本機 JMS 會話。 儘管如此,這些都是另一個帖子的主題。