领先一步
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会话。不过,这些内容都是另一篇文章的主题。