【RabbitMQ消息中间件】10.Spring-Rabbit项目剖析

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u013517797/article/details/79601121
之前我们详细介绍了RabbitMQ并且讲解了其5中队列模式,在期间编写了许多小例子。但是在真实的开发阶段,我们很少使用之前编写Demo时的编写方式,一个是不方便,一个是难以维护。所以,在企业级的开发中,一般是结合框架来进行开发的。

一、spring-rabbit项目介绍
在Java开发中,RabbitMQ的开发自然少不了与Spring框架的结合。本篇我们就来了解一下,Spring为我们提供的与RabbitMQ向结合的开源样例工程“Spring-Rabbit”,通过了解该开源工程,我们可以更加了解在实际开发中如何结合Spring来进行RabbitMQ业务的开发。

在Spring的官方网站“spring.io”中,我们可以看到spring团队发布的与spring相关的历届以及最新的框架产品:

【RabbitMQ消息中间件】10.Spring-Rabbit项目剖析

而本此我们要了解的,就是spring框架对AMQP协议的支持,即是“spring AMQP”的相关产品:

【RabbitMQ消息中间件】10.Spring-Rabbit项目剖析

点击进去之后,可以看到该开源项目的详细介绍:

【RabbitMQ消息中间件】10.Spring-Rabbit项目剖析

【RabbitMQ消息中间件】10.Spring-Rabbit项目剖析

可以在介绍中看到,该产品包含了两个部分,其中“spring-amqp”是一个基本的抽象实现,而“spring-rabbit”是一个基于RabbitMQ的一个具体实现。

其实就是说,“spring-amqp”是所有符合消息队列协议产品的基础实现,而实现该基础的产品有很多,“spring-rabbit”就是其中一种。

二、spring-rabbit使用
首先我们在Eclipse中创建一个maven项目:

【RabbitMQ消息中间件】10.Spring-Rabbit项目剖析

然后编辑POM文件,添加spring核心依赖、日志相关依赖:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.test.spring.rabbit</groupId>
<artifactId>spring_rabbit_test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>

<dependencies>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>

<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.4.0.RELEASE</version>
</dependency> 

<!-- spring核心依赖 -->
<dependency> 
<groupId>org.springframework</groupId> 
<artifactId>spring-core</artifactId> 
<version>4.2.5.RELEASE</version> 
</dependency>

<dependency> 
<groupId>org.springframework</groupId> 
<artifactId>spring-context</artifactId> 
<version>4.2.5.RELEASE</version> 
</dependency> 
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<version>3.0</version>
</configuration>
</plugin>
</plugins>
</build>
</project>

  


其中,spring-rabbit就是本次结合RabbitMQ的核心依赖jar,而amqp-client是rabbit通信的基础协议依赖。

然后我们在maven工程的“src/main/resources”下创建日志配置文件“logj4.properties”:

log4j.rootLogger=DEBUG,A1
log4j.logger.com.jack = DEBUG
log4j.logger.org.mybatis = DEBUG

log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n

  

下面是重点,在maven工程的“src/main/resources”下创建“rabbitmq-context,xml”来注入RabbitMQ需要的相关工厂类:
  
在该配置

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">

<!-- 定义RabbitMQ的连接工厂 -->
<rabbit:connection-factory 
host="127.0.0.1" port="5672" username="jack" password="jack"
virtual-host="/jack" />

<!-- 定义Rabbit模板,指定连接工厂以及定义exchange -->
<rabbit:template  />
<!-- 如果需要定义路由key,则需要添加routing-key参数指定路由key,如:routing-key="foo.bar" -->

<!-- MQ的管理,包括队列、交换器等 -->
<rabbit:admin connection-factory="connectionFactory" />

<!-- 定义队列,自动声明 -->
<rabbit:queue name="myQueue" auto-declare="true"/>

<!-- 定义交换器,自动声明 -->
<rabbit:fanout-exchange name="fanoutExchange" auto-declare="true">
<rabbit:bindings>
<rabbit:binding queue="myQueue"/>
</rabbit:bindings>
</rabbit:fanout-exchange>
<!-- 如果交换机为通配符类型,则需要在队列中定义通配符的key(pattern),如:pattern="foo.*" -->

<!-- 队列监听 -->
<rabbit:listener-container connection-factory="connectionFactory">
<rabbit:listener ref="foo" method="listen" queue-names="myQueue" />
</rabbit:listener-container>

<bean  />

</beans>

  

文件中,首先定义了RabbitMQ的连接工厂:

<!-- 定义RabbitMQ的连接工厂 -->
<rabbit:connection-factory 
host="127.0.0.1" port="5672" username="jack" password="jack"
virtual-host="/jack" />
而该连接工厂做的事情,和之前我们自己编写的连接工厂做的事情是一样的:
package cn.jack.rabbitmq.connection;
import java.io.IOException;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class ConnectionUtil {
public static Connection getConnection() throws IOException{
//定义连接工厂
ConnectionFactory factory = new ConnectionFactory();
//定义连接地址
factory.setHost("localHost");
//定义端口
factory.setPort(5672);
//设置账号信息,用户名、密码、vhost
factory.setVirtualHost("/jack");
factory.setUsername("jack");
factory.setPassword("jack");
// 通过工厂获取连接
Connection connection = factory.newConnection();
return connection;
}
}

  


都是定义一些列连接信息后,通过工厂获取RabbitMQ的连接对象。

然后下面的配置是定义Rabbit模板,指定连接工厂以及定义exchange(交换机):

<!-- 定义Rabbit模板,指定连接工厂以及定义exchange -->
<rabbit:template  />

  


该处做的事情,就是定义一个模板,指定其获取连接的连接工厂,和其绑定的交换机。
如果是路由或通配符那种需要定义发送消息的路由key的交换机,则需要再添加一个“routing-key”参数来指定路由key:

<!-- 如果需要定义路由key,则需要添加routing-key参数指定路由key -->
<rabbit:template 
exchange="fanoutExchange" routing-key="foo.bar" />

  


对于上面绑定的交换机,在下面有定义其详细配置:

<!-- 定义交换器,自动声明 -->
<rabbit:fanout-exchange name="fanoutExchange" auto-declare="true">
<rabbit:bindings>
<rabbit:binding queue="myQueue"/>
</rabbit:bindings>
</rabbit:fanout-exchange>

  


其中auto-declare是指,是否进行自动声明,即是如果交换机不存在时,spring会帮助创建该交换机,如果交换机存在则忽略。在里面的“rabbit:bindings”中配置的即是与该交换机绑定的队列。
有三种交换机类型“fanout-exchange”、“direct-exchange”以及“topic-exchange”,分别是“非路由交换机”、“路由交换机”以及“通配符交换机”,具体区别在之前的总结中已经介绍,这里不再赘述。
上面的配置是“fanout-exchange”类型的交换机,如果需要其他类型的,按照不同的前缀进行指定。下面是通配符交换机的配置方式:

<!-- 如果交换机为通配符类型,则需要在队列中定义通配符的key(pattern) -->
<rabbit:topic-exchange name="myExchange">
<rabbit:bindings>
<rabbit:binding queue="myQueue" pattern="foo.*" />
</rabbit:bindings>
</rabbit:topic-exchange>

  


首先将配置标签定义为“topic-exchange”,然后里面的队列要指定相关的key键,用以接收符合该key的消息。

对于名为“myQueue”队列也在上面定义了,并且也配置了自动声明:

<!-- 定义队列,自动声明 -->
<rabbit:queue name="myQueue" auto-declare="true"/>

  


在上面还有一个配置,是RabbitMQ的管理中心:

<!-- MQ的管理,包括队列、交换器等 -->
<rabbit:admin connection-factory="connectionFactory" />

  


它的作用就是管理交换机和队列的,对于交换机和路由器上面设置的“auto-declare”自动声明,是由上面的MQ管理中心来做自动声明的工作的。

上面所讲解的配置,其实已经完成了一个生产者所做的事情。那么最后就是配置消费者的相关设置,即是“队列监听”:

<!-- 队列监听 -->
<rabbit:listener-container connection-factory="connectionFactory">
<rabbit:listener ref="foo" method="listen" queue-names="myQueue" />
</rabbit:listener-container>

<bean  />

  


首先为队列监听配置一个连接工厂,然后在其中配置消费者的依赖id、执行监听的方法名以及接受消息的队列名称。

对于rabbit:listener中的ref中的对象id,在下面有一个bean的配置,会将foo对象加载到spring的bean容器中。

配置文件讲解完毕,我们基于该配置来创建一个生产者和消费者。
在“src/mian/java”下创建一个SpringMain类,在其中创建一个mian方法,编写生产者的逻辑:

package cn.jack.rabbitmq.spring;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringMain {
public static void main(String[] args) throws Exception {
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(
"classpath:rabbitmq-context.xml");
//Rabbit模板
RabbitTemplate template = ctx.getBean(RabbitTemplate.class);
//发送消息
template.convertAndSend("Hello,world!");
Thread.sleep(1000);//休眠1秒
ctx.close();//关闭容器
}
}

  


在该类中,首先使用ClassPathXmlApplicationContext获取xml配置的对象ctx,然后通过getBean获取“RabbitTemplate”类型的Rabbit模板,之后执行convertAndSend方法将消息发送至交换机或队列,最后执行ctx的close关闭容器。

其实上面我们业务层需要关注的就是获取RabbitTemplate并发送消息,之前的ClassPathXmlApplicationContext获取xml配置对象的操作在一般的企业工程中是不会这样写的,一般会进行包扫描和自动注解注入的方式获取相关的bean,无需直接操作ClassPathXmlApplicationContext获取xml配置对象:
消息发送类:

package com.training.rabbitmq.sender; 
import org.springframework.amqp.core.AmqpTemplate; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Component; 

@Component 
public class DemoRabbitMqSender { 

@Autowired 
private AmqpTemplate rabbitTemplate; 

public void send(String content) { 
System.out.println("Sender : " + content); 
this.rabbitTemplate.convertAndSend("hello", content); 
} 

} 

  


消息接收类

package com.training.rabbitmq.controller; 
import io.swagger.annotations.ApiOperation; 
import javax.servlet.http.HttpSession; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 
import org.springframework.web.bind.annotation.ResponseBody; 
import org.springframework.web.bind.annotation.RestController; 
import com.training.core.dto.ResultDataDto; 
import com.training.rabbitmq.sender.DemoRabbitMqSender; 

@RestController 
@RequestMapping(value="/rabbitmq") 
public class DemoRabbitMqController { 

@Autowired 
private DemoRabbitMqSender demoRabbitMqSender; 

/** 
* 发送测试消息队列 
*/ 
@ApiOperation(value="发送测试消息队列", notes="addEntity") 
@RequestMapping(value = "/addRabbitMq", method = RequestMethod.GET) 
public @ResponseBody ResultDataDto addEntity(HttpSession httpSession) { 
demoRabbitMqSender.send("Hello,World!"); 
return ResultDataDto.addAddSuccess(); 
} 
} 

  


上面的代码仅供参考,不使用于本篇创建的样例工程,因为这里没有集成Spring的其它环境(核心/web/mvc等),仅仅为了方便演示rabbit的整合,所以直接使用了ClassPathXmlApplicationContext。

对于消费者,它的编写就十分的简单。之前配置文件中配置了一个foo的bean:

<bean  />

  


所以我们下面在“src/mian/java”下创建cn.jack.rabbitmq.spring包下的Foo类:

package cn.jack.rabbitmq.spring;
public class Foo {
//具体执行业务的方法
public void listen(String foo){
System.out.println("消费者:"+foo);
}
}

  


代码十分简单,复杂的传输过程已经由spring通过配置文件的规则进行了处理。上面的listen方法即是在队列监听中配置的“method”参数:

<!-- 队列监听 -->
<rabbit:listener-container connection-factory="connectionFactory">
<rabbit:listener ref="foo" method="listen" queue-names="myQueue" />
</rabbit:listener-container>

  


下面我们运行SpringMain类的main方法,可以在控制台看到如下信息:

【RabbitMQ消息中间件】10.Spring-Rabbit项目剖析

然后到RabbitMQ的管理工具中查看队列:

【RabbitMQ消息中间件】10.Spring-Rabbit项目剖析

可以看到创建了一个新的名为“MyQueue”的队列。
至此,Spring与RabbitMQ结合的小样例工程编写完毕。

后期将会编写一个结合Web工程的RabbitMQ与Spring结合的实例,到时将使用包扫描和注解的方式注入Rabbit模板的实现。

转载请注明出处:http://blog.csdn.net/acmman/article/details/79601121
————————————————
版权声明:本文为CSDN博主「光仔December」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/acmman/article/details/79601121