领先一步
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");
}
}
接下来,您需要配置上下文以创建将消息路由到此 Bean 的 MessageListenerContainer。您会注意到,在此示例中,我使用的是 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,以及一个引导上下文的 main 方法。我已添加了来自此示例的 项目存档,以便您可以在需要时查看其余代码。
最后,您只需运行应用程序并查看输出。
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>
我想指出的一件事(根据我自己的痛苦经验)是确保不要将并发消费者与主题一起使用。请记住,在 JMS 主题中,所有消息都会传递给主题上的所有消费者。这意味着,如果您在主题上有多个并发消费者,它们都将接收相同的消息;通常这是您想要避免的事情。但是,如果您使用的是队列,则显然会以循环方式将每条新消息分派给消费者。
就是这样。它并不花哨,可能与您在某些时候可能编写的内容非常相似,但现在您只需使用它,而无需维护它。我还想说,这仅仅是冰山一角。MessageListenerContainer 能够参与事务、使用自定义线程池(例如应用程序服务器提供的线程池)以及新的 Spring TaskExecutor 抽象,甚至将本机 JMS 会话公开给消费者。不过,这些都是以后文章的主题。