版權聲明:本文為博主原創文章,遵循 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相關的歷屆以及最新的框架產品:
而本此我們要了解的,就是spring框架對AMQP協議的支持,即是“spring AMQP”的相關產品:
點擊進去之后,可以看到該開源項目的詳細介紹:
可以在介紹中看到,該產品包含了兩個部分,其中“spring-amqp”是一個基本的抽象實現,而“spring-rabbit”是一個基於RabbitMQ的一個具體實現。
其實就是說,“spring-amqp”是所有符合消息隊列協議產品的基礎實現,而實現該基礎的產品有很多,“spring-rabbit”就是其中一種。
二、spring-rabbit使用
首先我們在Eclipse中創建一個maven項目:
然后編輯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 id="connectionFactory" host="127.0.0.1" port="5672" username="jack" password="jack" virtual-host="/jack" /> <!-- 定義Rabbit模板,指定連接工廠以及定義exchange --> <rabbit:template id="amqpTemplate" connection-factory="connectionFactory" exchange="fanoutExchange" /> <!-- 如果需要定義路由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 id="foo" class="cn.jack.rabbitmq.spring.Foo" /> </beans>
文件中,首先定義了RabbitMQ的連接工廠:
<!-- 定義RabbitMQ的連接工廠 --> <rabbit:connection-factory id="connectionFactory" 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 id="amqpTemplate" connection-factory="connectionFactory" exchange="fanoutExchange" />
該處做的事情,就是定義一個模板,指定其獲取連接的連接工廠,和其綁定的交換機。
如果是路由或通配符那種需要定義發送消息的路由key的交換機,則需要再添加一個“routing-key”參數來指定路由key:
<!-- 如果需要定義路由key,則需要添加routing-key參數指定路由key --> <rabbit:template id="amqpTemplate" connection-factory="connectionFactory" 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="foo" class="cn.jack.rabbitmq.spring.Foo" />
首先為隊列監聽配置一個連接工廠,然后在其中配置消費者的依賴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 id="foo" class="cn.jack.rabbitmq.spring.Foo" />
所以我們下面在“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的管理工具中查看隊列:
可以看到創建了一個新的名為“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