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 社区所有即将举行的活动。

查看所有