今天来给大家介绍下rabbitmq的spring集成方式,现在绝大部分的组件基本上都能够与spring无缝集成,使用起来也非常方便。
下面,笔者分以下几个步骤进行介绍:
一、maven依赖
pom.xml依赖如下:
<!-- RabbitMQ -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.3.4</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
注意对于此版本的rabbitmq使用的spring版本是4.1.6,否则会解析报错的。
二、服务器配置rabbitmq.properties
rabbitmq服务器的配置rabbitmq.properties如下:
mq.host=127.0.0.1
mq.username=admin
mq.password=888888
mq.port=5672
# 前缀
mq.queue=qyk
注意rabbitmq的默认web访问端口是15672,而连接的端口号是5672,不要搞错了;当然你也可以修改这些默认配置~
三、spring集成配置如下
rabbitmqContext.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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-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/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<!-- 属性文件 -->
<util:properties id="appConfig" location="classpath:rabbitmq.properties"/>
<!-- 连接配置 -->
<rabbit:connection-factory id="connectionFactory" host="${mq.host}" username="${mq.username}"
password="${mq.password}" port="${mq.port}"/>
<rabbit:admin connection-factory="connectionFactory"/>
<!-- 消息队列客户端 -->
<rabbit:template id="amqpTemplate" exchange="${mq.queue}_exchange" connection-factory="connectionFactory"/>
<!-- queue 队列声明 -->
<!--
durable 是否持久化
exclusive 仅创建者可以使用的私有队列,断开后自动删除
auto-delete 当所有消费端连接断开后,是否自动删除队列 -->
<rabbit:queue id="test_queue" name="${mq.queue}_testQueue" durable="true" auto-delete="false" exclusive="false" />
<!-- 交换机定义 -->
<!-- 交换机:一个交换机可以绑定多个队列,一个队列也可以绑定到多个交换机上。如果没有队列绑定到交换机上,则发送到该交换机上的信息则会丢失。
**交换机的四种模式:
direct:转发消息到 routigKey 指定的队列。
topic:按规则转发消息(最灵活)。
headers:这个用得少
fanout:转发消息到所有绑定队列
-->
<rabbit:topic-exchange name="${mq.queue}_exchange" durable="true" auto-delete="false">
<rabbit:bindings>
<!-- 设置消息Queue匹配的pattern (direct模式为key) -->
<rabbit:binding queue="test_queue" pattern="${mq.queue}_pattern"/>
</rabbit:bindings>
</rabbit:topic-exchange>
<!-- 配置监听 消费者 -->
<bean name="rabbitmqConsumer" class="org.qiyongkang.spring.rabbitmq.Consumer"></bean>
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="auto">
<!--
queues 监听队列,多个用逗号分隔
ref 监听器 -->
<rabbit:listener queues="test_queue" ref="rabbitmqConsumer"/>
</rabbit:listener-container>
</beans>
这里注意,相关的命名空间需要引入进来!
这里面配置了一个消费者Consumer,如下:
/**
* Project Name:qyk_testSpring
* File Name:Consumer.java
* Package Name:org.qiyongkang.spring.rabbitmq
* Date:2017年3月6日上午10:22:58
* Copyright (c) 2017, Thinkive(http://www.thinkive.com/) All Rights Reserved.
*
*/
package org.qiyongkang.spring.rabbitmq;
import java.util.Map;
import org.qiyongkang.spring.util.SerializeUtil;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
/**
* ClassName:Consumer <br/>
* Function: TODO ADD FUNCTION. <br/>
* Reason: TODO ADD REASON. <br/>
* Date: 2017年3月6日 上午10:22:58 <br/>
* @author qiyongkang
* @version
* @since JDK 1.6
* @see
*/
public class Consumer implements MessageListener {
@SuppressWarnings("unchecked")
@Override
public void onMessage(Message message) {
Map<String, Object> content = (Map<String, Object>) SerializeUtil.toObject(message.getBody());
System.out.println("消费内容:" + content);
}
}
这里因为把消息对象放进去的时候是默认会序列化的,所以这里进行反序列化,SerializeUtil如下:
/**
* Project Name:qyk_testSpring
* File Name:SerializeUtil.java
* Package Name:org.qiyongkang.spring.util
* Date:2017年3月6日下午3:06:09
* Copyright (c) 2017, Thinkive(http://www.thinkive.com/) All Rights Reserved.
*
*/
package org.qiyongkang.spring.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* ClassName:SerializeUtil <br/>
* Function: TODO ADD FUNCTION. <br/>
* Reason: TODO ADD REASON. <br/>
* Date: 2017年3月6日 下午3:06:09 <br/>
*
* @author qiyongkang
* @version
* @since JDK 1.6
* @see
*/
public class SerializeUtil {
/**
* 对象转数组
*
* @param obj
* @return
*/
public static byte[] toByteArray(Object obj) {
byte[] bytes = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
bytes = bos.toByteArray();
oos.close();
bos.close();
} catch (IOException ex) {
ex.printStackTrace();
}
return bytes;
}
/**
* 数组转对象
*
* @param bytes
* @return
*/
public static Object toObject(byte[] bytes) {
Object obj = null;
try {
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
obj = ois.readObject();
ois.close();
bis.close();
} catch (IOException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
return obj;
}
}
另外,属性文件的加载是放在applicationContext.xml中,如下:
<!-- properties config -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="1"/>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>classpath:rabbitmq.properties</value>
</list>
</property>
</bean>
<!-- rabbitmq 缓存 -->
<import resource="rabbitmqContext.xml"/>
<!-- 定义扫描的包路劲 -->
<context:component-scan base-package="org.qiyongkang.spring.**" />
然后,我们再准备一个生产者Producer,如下:
/**
* Project Name:qyk_testSpring
* File Name:Producer.java
* Package Name:org.qiyongkang.spring.rabbitmq
* Date:2017年3月6日上午10:22:49
* Copyright (c) 2017, Thinkive(http://www.thinkive.com/) All Rights Reserved.
*
*/
package org.qiyongkang.spring.rabbitmq;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* ClassName:Producer <br/>
* Function: TODO ADD FUNCTION. <br/>
* Reason: TODO ADD REASON. <br/>
* Date: 2017年3月6日 上午10:22:49 <br/>
* @author qiyongkang
* @version
* @since JDK 1.6
* @see
*/
@Service
public class Producer {
@Autowired
private AmqpTemplate amqpTemplate;
public void sendQueue(String exchange_key, String queue_key, Object object) {
// convertAndSend 将Java对象转换为消息发送至匹配key的交换机中Exchange
amqpTemplate.convertAndSend(exchange_key, queue_key, object);
}
}
这里消费和生产的逻辑都非常简单,生产者不断给转换器发消息,然后路由选择相应的队列由消费者进行处理,关于转换器相关的概念笔者在后面进行介绍。
四、测试类
这里,我们准备一个spring的测试类,如下:
/**
* Project Name:qyk_testSpring
* File Name:ProducerTest.java
* Package Name:org.qiyongkang.spring.rabbitmq
* Date:2017年3月6日上午10:47:36
* Copyright (c) 2017, Thinkive(http://www.thinkive.com/) All Rights Reserved.
*
*/
package org.qiyongkang.spring.rabbitmq;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* ClassName:ProducerTest <br/>
* Function: TODO ADD FUNCTION. <br/>
* Reason: TODO ADD REASON. <br/>
* Date: 2017年3月6日 上午10:47:36 <br/>
* @author qiyongkang
* @version
* @since JDK 1.6
* @see
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class ProducerTest extends AbstractJUnit4SpringContextTests {
@Autowired
private Producer producer;
@Value("#{appConfig['mq.queue']}")
private String queueId;
@Test
public void testSendQueue() {
try {
Map<String, Object> map = new HashMap<String, Object>();
map.put("name", "qiyongkang");
//指定转换器以及路由key,就会被绑定相应路由key的消费者进行消费处理
producer.sendQueue(queueId + "_exchange", queueId + "_pattern", map);
} catch (Exception e) {
e.printStackTrace();
}
}
}
好了,所以的配置以及代码已经写好了,我们来进行测试看看。
运行测试类,可以看到控制台输出如下:
然后,看看web控制台,可以看到已经生产了相应的转换器和队列,如下:
好了,关于rabbitmq的集成就到了这儿了,配置起来还是比较清晰明了的~~