一. 认识JMS
1.1 概述
对于JMS,百度百科,是这样介绍的:JMS即Java消息服务(Java Message Service)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。
简短来说,JMS是一种与厂商无关的 API,用来访问消息收发系统消息。它类似于JDBC(Java Database Connectivity),提供了应用程序之间异步通信的功能。
JMS1.0是jsr 194里规定的规范(关于jsr规范,请点击)。目前最新的规范是JSR 343,JMS2.0。
好了,说了这么多,其实只是在说,JMS只是sun公司为了统一厂商的接口规范,而定义出的一组api接口。
1.2 JMS体系结构
描述如下:
- JMS提供者(JMS的实现者,比如activemq jbossmq等)
- JMS客户(使用提供者发送消息的程序或对象,例如在12306中,负责发送一条购票消息到处理队列中,用来解决购票高峰问题,那么,发送消息到队列的程序和从队列获取消息的程序都叫做客户)
- JMS生产者,JMS消费者(生产者及负责创建并发送消息的客户,消费者是负责接收并处理消息的客户)
- JMS消息(在JMS客户之间传递数据的对象)
- JMS队列(一个容纳那些被发送的等待阅读的消息的区域)
- JMS主题(一种支持发送消息给多个订阅者的机制)
1.3. JMS对象模型
- 连接工厂(connectionfactory)客户端使用JNDI查找连接工厂,然后利用连接工厂创建一个JMS连接。
- JMS连接 表示JMS客户端和服务器端之间的一个活动的连接,是由客户端通过调用连接工厂的方法建立的。
- JMS会话 session 标识JMS客户端和服务端的会话状态。会话建立在JMS连接上,标识客户与服务器之间的一个会话进程。
- JMS目的 Destinatio 又称为消息队列,是实际的消息源
- 生产者和消费者
- 消息类型,分为队列类型(优先先进先出)以及订阅类型
二. ActiveMQ安装和思路
2.1. ActiveMQ的安装
从官网下载安装包, http://activemq.apache.org/download.html 赋予运行权限 chmod +x,windows可以忽略此步 运行 ./active start | stop 启动后,activeMQ会占用两个端口,一个是负责接收发送消息的tcp端口:61616,一个是基于web负责用户界面化管理的端口:8161。这两个端口可以在conf下面的xml中找到。http服务器使用了jettry。这里有个问题是启动mq后,很长时间管理界面才可以显示出来。
==》启动MQ服务器:
根据操作系统不同,进入相应win64/win32位目录,双击activemq.bat启动MQ。
ActiveMQ默认启动时,启动了内置的jetty服务器,提供一个用于监控ActiveMQ的admin应用 进入ActionMQ服务监控地址:浏览器输入http://127.0.0.1:8161/admin
2.2 整合思路
Spring最厉害的地方就是它的Bean了,还有它特有的IOC(控制反转)和AOP(面向切面编程)技术。有了这些,我们就可以不用new关键字构造对象,同时,可以方便地使用注入往类中的属性进行初始化。如果你编写过ActiveMQ之类的JMS应用程序,无论对于消息的生产者还是消费者,最重要的接口有以下两个: 1.ConnectionFactory 2.Destination
ConnectionFactory是一切的基础,有了它才有了Connection,然后才有Session,只有通过Session对象,我们才能创建消息队列、构建生产者/消费者,继而发送/接收消息。 Destination是一切的归宿,它就像总线一样,生产者发出消息要发到它上面,消费者取消息也要从这上面取。
试想,如果这一切都能借助Spring强大的Bean管理的话,我们在编写程序的时候会更加的方便简洁。幸运的是,ActiveMQ官方提供了完美的Spring框架支持,一切只需要在xml文件中配置即可~
Spring官方提供了一个叫JmsTemplate的类,这个类就专门用来处理JMS的,在该类的Bean配置标签中有两个属性connectionFactory-ref和defaultDestination-ref正好对应JMS中的ConnectionFactory和Destination,如果你有兴趣查看源码的话,就可以发现JmsTemplate帮我们做了大量创建的工作,我们只需要用它来进行收发信息就ok了,而ActiveMQ官方也提供了对应的实现包。
三、Spring 整合
3.1 xml配置方式一
1<?xml version="1.0" encoding="UTF-8"?> 2<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" 3 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" 4 xmlns:jms="http://www.springframework.org/schema/jms" xmlns:task="http://www.springframework.org/schema/task" 5 xsi:schemaLocation=" 6 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd 7 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 8 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd 9 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd 10 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd 11 http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-2.5.xsd 12 http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"> 13 14 <bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory" p:brokerURL="tcp://localhost:61616"></bean> 15 <bean id="cachedConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory" p:targetConnectionFactory-ref="jmsConnectionFactory" p:sessionCacheSize="10"></bean> 16 <bean id="queue_test" class="org.apache.activemq.command.ActiveMQQueue"> 17 <!--消息队列名称 --> 18 <constructor-arg value="queue_test" /> 19 </bean> 20 <!-- Spring JMS Template --> 21 <!-- <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" p:connectionFactory-ref="cachedConnectionFactory" p:defaultDestination-ref="queue_test"></bean> --> 22 <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> 23 <constructor-arg ref="jmsConnectionFactory" /> 24 <!-- pub/sub模型(发布/订阅) --> 25 <property name="pubSubDomain" value="true" /> 26 <property name="messageConverter"> 27 <bean class="org.springframework.jms.support.converter.SimpleMessageConverter" /> 28 </property> 29 <!-- Session.AUTO_ACKNOWLEDGE 1消息自动签收 Session.CLIENT_ACKNOWLEDGE 2客户端调用acknowledge方法手动签收 Session.DUPS_OK_ACKNOWLEDGE 3不必必须签收,消息可能会重复发送 --> 30 <property name="sessionAcknowledgeMode" value="2" /> 31 </bean> 32 33 <jms:listener-container container-type="default" connection-factory="jmsConnectionFactory" acknowledge="auto"> 34 <jms:listener destination="queue_test" ref="simpleMsgListener" method="onMessage"></jms:listener> 35 </jms:listener-container> 36</beans> 37
3.2 xml配置方式二:
1<?xml version="1.0" encoding="UTF-8"?> 2<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" 3 xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" 4 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util" 5 xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:amq="http://activemq.apache.org/schema/core" xmlns:jms="http://www.springframework.org/schema/jms" 6 xsi:schemaLocation=" 7 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 8 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd 9 http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd 10 http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd 11 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd 12 http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd 13 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd 14 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd 15 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd 16 http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-4.0.xsd 17 http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.14.5.xsd 18 "> 19 20 <context:annotation-config /> 21 <context:component-scan base-package="com.troy" /> 22 23 <!-- 读取配置文件 --> 24 <bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 25 <property name="locations"> 26 <array> 27 <value>classpath:conf/spring-config.properties</value> 28 </array> 29 </property> 30 </bean> 31 32 <!-- 连接 activemq --> 33 <amq:connectionFactory id="amqConnectionFactory" brokerURL="${activemq_url}" userName="${activemq_username}" password="${activemq_password}" /> 34 35 <!-- 这里可以采用连接池的方式连接PooledConnectionFactoryBean --> 36 <bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory"> 37 <!-- 配置连接 --> 38 <property name="targetConnectionFactory" ref="amqConnectionFactory" /> 39 <!-- 会话的最大连接数 --> 40 <property name="sessionCacheSize" value="100" /> 41 </bean> 42 43 <!-- 定义消息队列topic类型,queue的方式差不多 --> 44 <bean id="topic" class="org.apache.activemq.command.ActiveMQTopic"> 45 <!-- 定义名称 --> 46 <constructor-arg index="0" value="topic" /> 47 </bean> 48 49 <!-- 配置JMS模板(topic),Spring提供的JMS工具类,它发送、接收消息。 --> 50 <!-- 为了测试发送消息,保留jmsTemplate的配置,实际不存在发送,只需要配置监听即可 --> 51 <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> 52 <property name="connectionFactory" ref="connectionFactory" /> 53 <property name="defaultDestination" ref="topic" /> 54 <!-- 非pub/sub模型(发布/订阅),true为topic,false为queue --> 55 <property name="pubSubDomain" value="true" /> 56 </bean> 57 58 <!-- 监听方式,这种方式更实用,可以一直监听消息 --> 59 <bean id="topicMessageListen" class="com.troy.activemq.TopicMessageListen" /> 60 <bean id="defaultMessageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> 61 <property name="connectionFactory" ref="connectionFactory" /> 62 <!-- 注册activemq名称 --> 63 <property name="destination" ref="topic" /> 64 <property name="messageListener" ref="topicMessageListen" /> 65 </bean> 66 67</beans> 68 69 70 71 72import javax.jms.JMSException; 73import javax.jms.Message; 74import javax.jms.MessageListener; 75import javax.jms.TextMessage; 76 77import org.springframework.stereotype.Component; 78@Component(value = "simpleMsgListener") 79public class SimpleMsgListener implements MessageListener { 80 //收到信息时的动作 81 @Override 82 public void onMessage(Message message) { 83 TextMessage textMessage = (TextMessage) message; 84 try { 85 System.out.println("收到的信息:" + textMessage.getText()); 86 } catch (JMSException e) { 87 e.printStackTrace(); 88 } 89 } 90} 91
四、spring boot配置
4.1、引入依赖配置
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-activemq</artifactId> 4 </dependency> 5 <dependency> 6 <groupId>org.apache.activemq</groupId> 7 <artifactId>activemq-pool</artifactId> 8 </dependency> 9
4.2 application.properties配置
1# ActiveMQ-------------------------------------------------------------------------------------------------------------- 2# Specify if the default broker URL should be in memory. Ignored if an explicit broker has been specified. 3#spring.activemq.in-memory=false 4# URL of the ActiveMQ broker. Auto-generated by default. For instance `tcp://localhost:61616` 5spring.activemq.broker-url=tcp://127.0.0.1:61616 6# Login user of the broker. 7spring.activemq.user=admin 8# Login password of the broker. 9spring.activemq.password=admin 10# Trust all packages. 11#spring.activemq.packages.trust-all=false 12# Comma-separated list of specific packages to trust (when not trusting all packages). 13#spring.activemq.packages.trusted= 14# See PooledConnectionFactory. 15#spring.activemq.pool.configuration.*= 16# Whether a PooledConnectionFactory should be created instead of a regular ConnectionFactory. 17spring.activemq.pool.enabled=true 18# Maximum number of pooled connections. 19spring.activemq.pool.max-connections=50 20# Connection expiration timeout in milliseconds. 21spring.activemq.pool.expiry-timeout=10000 22# Connection idle timeout in milliseconds. 23spring.activemq.pool.idle-timeout=30000 24spring.jms.pub-sub-domain=false 25 26
4.3 boot类配置
1import javax.jms.Queue; 2import javax.jms.Topic; 3 4import org.apache.activemq.ActiveMQConnectionFactory; 5import org.apache.activemq.command.ActiveMQQueue; 6import org.apache.activemq.command.ActiveMQTopic; 7import org.springframework.beans.factory.annotation.Value; 8import org.springframework.context.annotation.Bean; 9import org.springframework.context.annotation.Configuration; 10import org.springframework.jms.config.DefaultJmsListenerContainerFactory; 11import org.springframework.jms.config.JmsListenerContainerFactory; 12 13@Configuration 14public class ActiveMQConfig { 15 @Value("${queueName}") 16 private String queueName; 17 18 @Value("${topicName}") 19 private String topicName; 20 21 @Value("${spring.activemq.user}") 22 private String usrName; 23 24 @Value("${spring.activemq.password}") 25 private String password; 26 27 @Value("${spring.activemq.broker-url}") 28 private String brokerUrl; 29 30 /** 31 * <p>@describe: 配置队列 </p> 32 * <p>@author: whh </p> 33 * <p>@date: 2019年8月9日 下午5:56:22</p> 34 * @return 35 */ 36 @Bean 37 public Queue queue() { 38 return new ActiveMQQueue(queueName); 39 } 40 41 /** 42 * <p>@describe:配置主题 </p> 43 * <p>@author: whh </p> 44 * <p>@date: 2019年8月9日 下午5:56:40</p> 45 * @return 46 */ 47 @Bean 48 public Topic topic() { 49 return new ActiveMQTopic(topicName); 50 } 51 52 /** 53 * <p>@describe:连接工厂 </p> 54 * <p>@author: whh </p> 55 * <p>@date: 2019年8月9日 下午5:57:14</p> 56 * @return 57 */ 58 @Bean 59 public ActiveMQConnectionFactory connectionFactory() { 60 return new ActiveMQConnectionFactory(usrName, password, brokerUrl); 61 } 62 63 /** 64 * <p>@describe: 监听队列bean </p> 65 * <p>@author: whh </p> 66 * <p>@date: 2019年8月9日 下午5:57:43</p> 67 * @param connectionFactory 68 * @return 69 */ 70 @Bean 71 public JmsListenerContainerFactory<?> jmsListenerContainerQueue(ActiveMQConnectionFactory connectionFactory) { 72 DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory(); 73 bean.setConnectionFactory(connectionFactory); 74 return bean; 75 } 76 77 /** 78 * <p>@describe:监听主题 bean </p> 79 * <p>@author: whh </p> 80 * <p>@date: 2019年8月9日 下午5:58:17</p> 81 * @param connectionFactory 82 * @return 83 */ 84 @Bean 85 public JmsListenerContainerFactory<?> jmsListenerContainerTopic(ActiveMQConnectionFactory connectionFactory) { 86 DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory(); 87 //设置为发布订阅方式, 默认情况下使用的生产消费者方式 88 bean.setPubSubDomain(true); 89 bean.setConnectionFactory(connectionFactory); 90 return bean; 91 } 92}
五、发送者和接收者使用
5.1 发送者
1import javax.jms.Destination; 2import javax.jms.JMSException; 3import javax.jms.Message; 4import javax.jms.Session; 5 6import org.slf4j.Logger; 7import org.slf4j.LoggerFactory; 8import org.springframework.beans.factory.annotation.Autowired; 9import org.springframework.jms.core.JmsTemplate; 10import org.springframework.jms.core.MessageCreator; 11import org.springframework.stereotype.Service; 12 13 14@Service 15public class JmsSendDemo { 16 private static final Logger LOGGER = LoggerFactory.getLogger(JmsSendDemo.class); 17 @Autowired 18 private JmsTemplate jmsTemplate; 19 20 public void execute(){ 21 LOGGER.debug("执行定时任务-----------------直接违规数据定时:下发至医院端首页进行展示-----------------"); 22 23 Destination destination = (Destination) AppContext.getBean("queue_test"); 24 jmsTemplate.convertAndSend("mailbox", new Email("info@example.com", "Hello")); 25 //启动另一个方法 26 boolean flag=true; 27 LOGGER.debug("执行定时任务结束------------------------------"); 28 } 29 30 31 public void sendMessage(Destination destination,final String message) { 32 LOGGER.debug("发送消息到AMQ,消息内容为:"+message); 33 jmsTemplate.send(destination, new MessageCreator() { 34 @Override 35 public Message createMessage(Session session) throws JMSException { 36 return session.createTextMessage(message); 37 } 38 }); 39 } 40} 41
5.1 接收者
1import org.springframework.jms.annotation.JmsListener; 2import org.springframework.stereotype.Component; 3 4@Component 5public class Receiver { 6 @JmsListener(destination = "mailbox", containerFactory = "myFactory") 7 public void receiveMessage(Email email) { 8 System.out.println("Received <" + email + ">"); 9 } 10 11}