Spring 2.0 的 JMS 改進

工程 | Ben Hale | 2006年4月9日 | ...

隨著 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的子類一起使用,例如DefaultMessageListenerContainerSimpleMessageListenerContainerServerSessionMessageListener

我們來看看如何使用這些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會話。不過,這些內容都是另一篇文章的主題。

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有