项目最终目录结构如下:
项目源码:http://download.youkuaiyun.com/detail/u011983531/9239911
1、在src目录下建立spring-jms.xml文件
下面对文件中各个部分的配置进行详解
(1)配置ConnectionFactory
Spring为我们提供了SingleConnectionFactory和CachingConnectionFactory。SingleConnectionFactory对于建立JMS服务器链接的请求会一直返回同一个链接,并且会忽略Connection的close方法调用。CachingConnectionFactory继承了SingleConnectionFactory,所以它拥有SingleConnectionFactory的所有功能,同时它还新增了缓存功能,它可以缓存Session、MessageProducer和MessageConsumer。但是Spring提供的ConnectionFactory只是Spring用于管理ConnectionFactory的,真正创建JMS服务器链接的ConnectionFactory还得由JMS服务厂商(这里我们选择ActiveMQ)提供,并且需要把它注入到Spring提供的ConnectionFactory中。
<!-- 真正的可产生连接的连接工厂 -->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616"></property>
</bean>
<!-- Spring用于管理ConnectionFactory的ConnectionFactory -->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory" ref="targetConnectionFactory"></property>
</bean>
(2)配置生产者
生产者负责发送消息到JMS服务器,这通常对应一个业务逻辑类。在业务逻辑类中利用Spring为我们提供的JmsTemplate类发送消息,所以配置生产者主要就是配置JmsTemplate。对于生产者而言,在发送消息的时候要知道往哪里发,为此,在配置JmsTemplate的时候需要往里面注入一个Spring提供的ConnectionFactory对象。
在真正利用JmsTemplate进行消息发送的时候,我们需要知道消息发送的目的地,即destination。在ActiveMQ中实现了两种类型的Destination,一个是点对点的ActiveMQQueue,另一个就是订阅/发布模式的ActiveMQTopic。
业务逻辑类代码如下:
package com.ghs.jms.service;
import javax.annotation.Resource;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import org.springframework.jms.JmsException;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Component;
@Component("messageService")
public class MessageService {
@Resource(name="jmsTemplate")
private JmsTemplate jmsTemplate;
public void sendMessage(Destination destination,final String msg){
try {
jmsTemplate.send(destination,new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(msg);
}
});
} catch (JmsException e) {
e.printStackTrace();
}
}
}
<!-- Spring提供的JMS工具类,可以用来接收、发送消息 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory"></property>
</bean>
<!-- 目的地队列 -->
<bean id="destination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg>
<value>FirstQueue</value>
</constructor-arg>
</bean>
(3)配置消费者
生产者往指定目的地Destination发送消息后,接下来就是消费者对指定目的地的消息进行消费了。那么消费者是如何知道有生产者发送消息到指定目的地Destination了呢?这是通过Spring为我们封装的消息监听容器MessageListenerContainer实现的,它负责接收信息,并把接收到的信息分发给真正的MessageListener进行处理。每个消费者对应每个目的地都需要有对应的MessageListenerContainer。对于消息监听容器而言,除了要知道监听哪个目的地之外,还需要知道到哪里去监听,也就是说它还需要知道去监听哪个JMS服务器,这是通过在配置MessageConnectionFactory的时候往里面注入一个ConnectionFactory来实现的。所以我们在配置一个MessageListenerContainer的时候有三个属性必须指定,一个是表示从哪里监听的ConnectionFactory;一个是表示监听什么的Destination;一个是接收到消息以后进行消息处理的MessageListener。
Spring一共为我们提供了两种类型的MessageListenerContainer:SimpleMessageListenerContainer和DefaultMessageListenerContainer。
SimpleMessageListenerContainer会在一开始的时候就创建一个会话session和消费者Consumer,并且会使用标准的JMS MessageConsumer.setMessageListener()方法注册监听器让JMS提供者调用监听器的回调函数。它不会动态的适应运行时需要和参与外部的事务管理。兼容性方面,它非常接近于独立的JMS规范,但一般不兼容Java EE的JMS限制。
大多数情况下我们还是使用的DefaultMessageListenerContainer,跟SimpleMessageListenerContainer相比,DefaultMessageListenerContainer会动态的适应运行时需要,并且能够参与外部的事务管理。它很好的平衡了对JMS提供者要求低、先进功能如事务参与和兼容Java EE环境。
定义处理消息的MessageListener,如下:
package com.ghs.jms.listener;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
public class ConsumerMessageListener implements MessageListener {
@Override
public void onMessage(Message message) {
TextMessage msg = (TextMessage) message;
try {
System.out.println("收到:"+msg.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
<!-- 目的地队列 -->
<bean id="destination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg>
<value>FirstQueue</value>
</constructor-arg>
</bean>
<!-- 消息监听器 -->
<bean id="messageListener" class="com.ghs.jms.listener.ConsumerMessageListener">
</bean>
<!-- 消息监听容器 -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"></property>
<property name="destination" ref="destination"></property>
<property name="messageListener" ref="messageListener"></property>
</bean>
2、单元测试
定义测试类:
package com.ghs.jms.test;
import javax.annotation.Resource;
import javax.jms.Destination;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.ghs.jms.service.MessageService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring-jms.xml")
public class MessageServiceTest {
@Resource(name="destination")
private Destination destination;
@Resource(name="messageService")
private MessageService messageService;
@Test
public void testSend(){
for(int i=0; i<10; i++){
String text = "第"+i+"条消息";
System.out.println("发送:"+text);
messageService.sendMessage(destination, text);
}
}
}
测试结果:
发送:第0条消息
收到:第0条消息
发送:第1条消息
收到:第1条消息
发送:第2条消息
收到:第2条消息
发送:第3条消息
收到:第3条消息
发送:第4条消息
收到:第4条消息
发送:第5条消息
收到:第5条消息
发送:第6条消息
收到:第6条消息
发送:第7条消息
收到:第7条消息
发送:第8条消息
收到:第8条消息
发送:第9条消息
收到:第9条消息