領先一步
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");
}
}
接下來,您需要配置您的上下文以建立一個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://:61616" />
</bean>
</beans>
我暫時跳過它,但顯然您需要啟動一個MQ,以及一個引導您上下文的主方法。我已經添加了一個本示例的專案歸檔,以便您在需要時可以檢視其餘程式碼。
最後,您只需執行您的應用程式並檢視輸出。
Example Message
需要注意的一點是,到目前為止,我們一直在處理只有一個消費者執行緒的非同步接收。可以使用MessageListenerContainer的併發消費者屬性將您的消費者多執行緒化(請記住,您仍然需要使它們無狀態或執行緒安全)。
<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>
我想要指出的一點是(根據我自己的痛苦經歷),請確保不要在主題(Topic)上使用併發消費者。請記住,在JMS主題中,所有訊息都會傳遞給主題上的所有消費者。這意味著,如果您在主題上有併發消費者,所有這些消費者都將收到相同的訊息;這通常是您想要避免的。但是,如果您使用佇列,顯然這會將每條新訊息以輪詢的方式分派給消費者。
所以,您看。它不是很花哨,可能與您在某個時候編寫的非常相似,但現在您只需使用它,而無需維護它。我還想說這只是冰山一角。MessageListenerContainers能夠參與事務,使用自定義執行緒池(如應用程式伺服器提供的執行緒池)以及新的Spring TaskExecutor抽象,甚至向消費者公開原生JMS會話。不過,這些內容都是另一篇文章的主題。