本机环境
Win10 64位
Spring 4.3.12
ActiveMQ 5.15.5
ActiveMQ安装
http://activemq.apache.org/
1. 下载对应文件解压后执行对应操作系统的activemq.bat
2. 打开浏览器进入 http://localhost:8161/admin/
3. 看到内容就说明ActiveMQ服务已启动 (用户名&密码默认为admin)
spring + ActiveMQ
pom.xml 配置
引入如下的包
<!-- spring整合消息队列activemq -->
<!-- 要和自己下的activemq版本对应 -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-spring</artifactId>
<version>5.15.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- 这个不知道干啥的,反正不加会报错! -->
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-spring</artifactId>
<version>4.5</version>
</dependency>
spring-applicationContext.xml 配置
<!-- activemq配置 -->
<bean id="amqConnectionFactory" class="org.apache.activemq.spring.ActiveMQConnectionFactory">
<property name="brokerURL" value="${activemq.url}"/>
<property name="userName" value="${activemq.username}" />
<property name="password" value="${activemq.password}" />
<!-- trustedPackages 这个属性定义信任的java包,要使用MQ传对象就要将对象对应的类所在的包加入list,不然会报错 -->
<property name="trustedPackages">
<list>
<value>com.shu.yzy.dto</value>
<value>java.lang</value>
<value>java.util</value>
</list>
</property>
</bean>
<!-- 连接 activemq-->
<!-- <amq:connectionFactory id="amqConnectionFactory" brokerURL="${activemq.url}" userName="${activemq.username}" password="${activemq.password}"/> -->
<!-- 这里可以采用连接池的方式连接PooledConnectionFactoryBean -->
<bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<!-- 配置连接 -->
<property name="targetConnectionFactory" ref="amqConnectionFactory"/>
<!-- 会话的最大连接数 -->
<property name="sessionCacheSize" value="100"/>
</bean>
<!-- 定义消息队列topic类型,queue的方式差不多 topic对应订阅模式-->
<bean id="topic" class="org.apache.activemq.command.ActiveMQTopic">
<!-- 定义名称 -->
<constructor-arg index="0" value="topic"/>
</bean>
<!-- 定义消息队列(Queue) queue对应点对点模式-->
<bean id="demoQueueDestination" class="org.apache.activemq.command.ActiveMQQueue">
<!-- 设置消息队列的名字 -->
<constructor-arg>
<value>${activemq.queue}</value>
</constructor-arg>
</bean>
<!-- 配置JMS模板(queue),Spring提供的JMS工具类,它发送、接收消息。 -->
<!-- 为了测试发送消息,保留jmsTemplate的配置,实际不存在发送,只需要配置监听即可 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="defaultDestination" ref="demoQueueDestination"/>
<!-- 非pub/sub模型(发布/订阅),true为topic,false为queue -->
<property name="pubSubDomain" value="false"/>
</bean>
<!-- 显示注入消息监听容器(Queue),配置连接工厂,监听的目标是demoQueueDestination,监听器是上面定义的监听器 -->
<bean id="seckillMessageListener" class="com.shu.yzy.activemq.SeckillMessageListener"></bean>
<bean id="queueListenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="demoQueueDestination" />
<!-- 这个是DIY的Listener对象 用于监听队列并消费队列元素 -->
<property name="messageListener" ref="seckillMessageListener" />
</bean>
activemq.properties 配置
要注意消息队列的默认服务访问端口是61616 别填上面的后台管理端口8161
activemq.url=tcp://localhost:61616
activemq.username=admin
activemq.password=admin
activemq.queue=seckill
简单实现一下MQ功能
Producer 实现
ProducerService 代码清单
package com.shu.yzy.activemq.service;
import javax.jms.Destination;
import com.shu.yzy.dto.SeckillAction;
/**
* @author Administrator
*
*/
public interface MQProducerService {
/** 发送一个对象到指定的目标(Queue或者Topic)
* @param destination 目的地
* @param sa 数据传输对象
*/
void sendSeckillMessage(Destination destination, final SeckillAction sa);
/** 发送一个对象到默认的destination 可以通过jmsTemplate自动获取
* @param sa 数据传输对象
*/
void sendSeckillMessage(final SeckillAction sa);
}
ProducerServiceImpl 代码清单
package com.shu.yzy.activemq.service.impl;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;
import com.shu.yzy.activemq.service.MQProducerService;
import com.shu.yzy.dto.SeckillAction;
/**
* @author Administrator
*
*/
@Service("mqProducerService")
public class MQProducerServiceImpl implements MQProducerService{
@Autowired
JmsTemplate jmsTemplate;
Logger logger = Logger.getLogger(MQProducerServiceImpl.class);
@Override
public void sendSeckillMessage(Destination destination, SeckillAction sa) {
logger.info("向队列" + destination.toString() + "发送对象" + sa.getClass());
jmsTemplate.send(destination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
ObjectMessage om = session.createObjectMessage(sa);
return om;
}
});
}
@Override
public void sendSeckillMessage(SeckillAction sa) {
Destination dest = jmsTemplate.getDefaultDestination();
sendSeckillMessage(dest, sa);
}
}
Listener实现
package com.shu.yzy.activemq;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.TextMessage;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import com.shu.yzy.dto.SeckillAction;
import com.shu.yzy.service.SeckillService;
/** 从消息队列监听器
* @author Administrator
*
*/
public class SeckillMessageListener implements MessageListener{
private static Logger logger = Logger.getLogger(SeckillMessageListener.class);
@Override
public void onMessage(Message msg) {
if(msg instanceof ObjectMessage) {
ObjectMessage tm = (ObjectMessage) msg;
try {
logger.info("从消息队列中消费一个对象!!");
SeckillAction sa = (SeckillAction)tm.getObject();
logger.info(sa.toString());
//具体的业务逻辑
//seckillService.consumeSeckillAction(sa);
} catch (JMSException e) {
e.printStackTrace();
}
} else if(msg instanceof TextMessage) {
//do sth....
}
}
}
单元测试类
package com.shu.yzy.test;
import java.util.Date;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import com.shu.yzy.activemq.service.MQProducerService;
import com.shu.yzy.dto.SeckillAction;
/*单元测试类*/
@RunWith(org.springframework.test.context.junit4.SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class UnitTest {
@Resource
MQProducerService mqProducerService;
@Test
public void testRole() {
Long sid = new Long(1000);
Integer uid = 1;
SeckillAction sa = new SeckillAction(sid, uid, new Date());
mqProducerService.sendSeckillMessage(sa);
}
}
运行结果
2018-09-06 20:14:43,667 [main] INFO [com.shu.yzy.activemq.service.impl.MQProducerServiceImpl] - 向队列queue://seckill发送对象class com.shu.yzy.dto.SeckillAction
2018-09-06 20:14:43,750 [queueListenerContainer-1] INFO [com.shu.yzy.activemq.SeckillMessageListener] - 从消息队列中消费一个对象!!
2018-09-06 20:14:43,753 [queueListenerContainer-1] INFO [com.shu.yzy.activemq.SeckillMessageListener] - SeckillAction [seckill_id=1000, id=1, now=Thu Sep 06 20:14:43 CST 2018]