1.基础知识
图1 同步通信和异步通信通信过程示意图
RMI使用的是同步通信,JMS使用的是异步通信。从图1可以看出异步通信的好处就是减少了不必要的等待,提高了效率。
JMS中有两个主要的概念:消息代理(message broker)和目的地(destination)。
消息代理指的概念: 发送者将消息发送到消息代理服务器,代理服务器收到消息后提示接收端去接收消息,消息代理就起了控制消息流转的作用,Apache 的activemq就是一种消息代理服务器。
目的地指的概念: 消息发送的目标地址,也是消息接收者获取消息的目标地址,对应于queue和topic中的一种,并且要指定具体的名称。
JMS通信又分为点对点和点对多2种形式。
点对点通信形式如图12.3所示,采用javax.jms.Queue表示,点多多通信形式如图12.4所示,采用javax.jms.Topic表示。
同步通信的缺点,这也是使用JMS所能够解决的:
2.Spring对JMS的支持
为了消除重复冗余的JMS代码,如建立连接,异常处理等,我们应该使用Spring所提供的JmsTemplate来进行消息的发送与接收。
和JDBCTemplate类似,JmsTemplate将捕获JMS所抛出的检查时异常,进行封装并以非检查时异常的形式进行抛出,我们就不必须对异常进行捕获。
3.示例代码
JMS只是一种标准,Apache的activemq是其多种实现中的一种,下面将基于activemq来做示例。
示例代码将分为发送端代码,接收端代码,XML配置和测试类4部分。
首先要下载apache-activemq,运行apache-activemq-5.10.0-bin[1]apache-activemq-5.10.0inwin32下面的activemq.bat启动JMS服务,确保已经正常启动,61616端口未被占用。
3.1 发送端代码
发送消息的代码,注意要使用JmsTemplate:
@Component("producerServiceImpl") public class ProducerServiceImpl implements ProducerService { private JmsTemplate jmsTemplate; public void sendMessage(Destination destination, final String message) { jmsTemplate.send(destination, new MessageCreator() { public Message createMessage(Session session) throws JMSException { return session.createTextMessage(message); } }); } public JmsTemplate getJmsTemplate() { return jmsTemplate; } @Resource public void setJmsTemplate(JmsTemplate jmsTemplate) { this.jmsTemplate = jmsTemplate; } }
3.2 接收端代码
使用EJB的消息驱动来处理消息的代码如下,这样onMessage方法不会阻塞,但是要绑定MessageListener接口:
使用Spring所支持JMS接收消息的配置如下,比使用JmsTemplate和集成MessageListener接口要好:
需要在配置文件中做如下的配置:
配置一个listener-container,将消息接收端的bean放在其中
3.3.XML配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:jms="http://www.springframework.org/schema/jms" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd"> <context:component-scan base-package="com.tiantian" /> <!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 --> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 --> <property name="connectionFactory" ref="connectionFactory"/> </bean> <!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供--> <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61616"/> </bean> <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory --> <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory"> <!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory --> <property name="targetConnectionFactory" ref="targetConnectionFactory"/> </bean> <!--这个是队列目的地--> <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg> <value>queue</value> </constructor-arg> </bean> <!-- 消息监听器 --> <bean id="consumerMessageListener" class="com.tiantian.springintejms.listener.ConsumerMessageListener"/> <!-- 消息监听容器 --> <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="connectionFactory" /> <property name="destination" ref="queueDestination" /> <property name="messageListener" ref="consumerMessageListener" /> </bean> </beans>
3.4.测试类代码
测试的代码如下所示:
import javax.jms.Destination; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.tiantian.springintejms.service.ProducerService; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("/applicationContext.xml") public class ProducerConsumerTest { @Autowired private ProducerService producerService; @Autowired @Qualifier("queueDestination") private Destination destination; @Test public void testSend() { for (int i=0; i<2; i++) { producerService.sendMessage(destination, "你好,生产者!这是消息:" + (i+1)); } } }