SpringBoot高级-消息-RabbitTemplate发送接受消息&序列化机制

引入了spring-boot-starter-amqp模块,他引入了spring-messaging模块,包括引入了spring-rabbit模块,怎么配置使用呢,


	org.springframework
	spring-messaging


SpringBoot主要是引入了相关场景依赖,一切都是自动配置的,我们可以来分析一下原理,rabbitmq给我们自动配置了哪些

  4.0.0
  com.learn
  springboot-02-amqp
  0.0.1-SNAPSHOT
  jar
  
	
		org.springframework.boot
		spring-boot-starter-parent
		1.5.12.RELEASE
		 
	
	
	
		UTF-8
		UTF-8
		1.8
		3.0.9.RELEASE
		2.2.2
	
	
	
		
			org.springframework.boot
			spring-boot-starter-amqp
		
		
			org.springframework.boot
			spring-boot-starter-web
		

		
			org.springframework.boot
			spring-boot-starter-test
			test
		
	
	
	
	
	    
	        
	            org.springframework.boot
	            spring-boot-maven-plugin
	        
	    
	
  
  
我们就找一下和rabbitmq相关的类,有一个叫RabbitAutoConfiguration,这个自动配置类里面帮我们做了什么,

我们在这记录一下,自动配置类,我们配了哪些,进来看,这一块我们配了连接工厂,这是人家帮我们配好的连接

工厂CachingConnectionFactory,有自动配置了连接工厂,从这里可以获取,Rabbitmq的连接,比如我们的主机地址,

用户名,密码,还有VisualHost等等

@Configuration
@ConditionalOnMissingBean(ConnectionFactory.class)
protected static class RabbitConnectionFactoryCreator {

	@Bean
	public CachingConnectionFactory rabbitConnectionFactory(RabbitProperties config)
			throws Exception {
		RabbitConnectionFactoryBean factory = new RabbitConnectionFactoryBean();
		if (config.determineHost() != null) {
			factory.setHost(config.determineHost());
		}
		factory.setPort(config.determinePort());
		if (config.determineUsername() != null) {
			factory.setUsername(config.determineUsername());
		}
		if (config.determinePassword() != null) {
			factory.setPassword(config.determinePassword());
		}
		if (config.determineVirtualHost() != null) {
			factory.setVirtualHost(config.determineVirtualHost());
		}
		if (config.getRequestedHeartbeat() != null) {
			factory.setRequestedHeartbeat(config.getRequestedHeartbeat());
		}
		RabbitProperties.Ssl ssl = config.getSsl();
		if (ssl.isEnabled()) {
			factory.setUseSSL(true);
			if (ssl.getAlgorithm() != null) {
				factory.setSslAlgorithm(ssl.getAlgorithm());
			}
			factory.setKeyStore(ssl.getKeyStore());
			factory.setKeyStorePassphrase(ssl.getKeyStorePassword());
			factory.setTrustStore(ssl.getTrustStore());
			factory.setTrustStorePassphrase(ssl.getTrustStorePassword());
		}
		if (config.getConnectionTimeout() != null) {
			factory.setConnectionTimeout(config.getConnectionTimeout());
		}
		factory.afterPropertiesSet();
		CachingConnectionFactory connectionFactory = new CachingConnectionFactory(
				factory.getObject());
		connectionFactory.setAddresses(config.determineAddresses());
		connectionFactory.setPublisherConfirms(config.isPublisherConfirms());
		connectionFactory.setPublisherReturns(config.isPublisherReturns());
		if (config.getCache().getChannel().getSize() != null) {
			connectionFactory
					.setChannelCacheSize(config.getCache().getChannel().getSize());
		}
		if (config.getCache().getConnection().getMode() != null) {
			connectionFactory
					.setCacheMode(config.getCache().getConnection().getMode());
		}
		if (config.getCache().getConnection().getSize() != null) {
			connectionFactory.setConnectionCacheSize(
					config.getCache().getConnection().getSize());
		}
		if (config.getCache().getChannel().getCheckoutTimeout() != null) {
			connectionFactory.setChannelCheckoutTimeout(
					config.getCache().getChannel().getCheckoutTimeout());
		}
		return connectionFactory;
	}

}

都是从config里面得到的,config就是我们的RabbitProperties,我们点进来

@ConfigurationProperties(prefix = "spring.rabbitmq")
public class RabbitProperties {

这里面封装了Rabbitmq的所有配置,那我们就来配置他们,找到我们的配置文件,

spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.port=5672
#spring.rabbitmq.virtual-host=

首先rabbitmq的主机地址,我们写上我们的ip地址,http不用写,http是web的访问地址,还有rabbitmq的用户名,我们也写上,

guest,guest用户,还有我们的密码,也叫guest,还有rabbitmq的端口,这个端口呢,不写默认就是5672,这个可以不写,virtual-host

属性我们之前有说过,是一定要指定的,我们也可以不指定,virtual-host如果我们不写,如果是空字符串也是访问"/"

/**
 * If addresses have been set and the first address has a virtual host it is returned.
 * Otherwise returns the result of calling {@code getVirtualHost()}.
 * @return the virtual host or {@code null}
 * @see #setAddresses(String)
 * @see #getVirtualHost()
 */
public String determineVirtualHost() {
	if (CollectionUtils.isEmpty(this.parsedAddresses)) {
		return getVirtualHost();
	}
	Address address = this.parsedAddresses.get(0);
	return address.virtualHost == null ? getVirtualHost() : address.virtualHost;
}

都可以通过配置文件来知道,这个配置我们基本写完,但是我们如何给rabbitmq发送以及接收消息呢,我们可以看自动配置,

这个自动配置除了看工厂,它还会给我们放一个RabbitTemplate,给容器中加一个他,给RabbitMQ接收和发送消息的,就像以前

的JdbcTemplate,RedisTemplate,那么他还有什么呢,还可以来这里继续来看,除了RabbitTemplate,他还放了AmqpAdmin,这

AmqpAdmin呢,它是Rabbitmq的管理组件,Rabbitmq系统管理组件,他不来发消息和收消息,可以帮我们声明一个队列,创建

一个交换器,这个是自动配置给我们放的组件,我们就用RabbitTemplate,我在这个测试类里面
我们尝试给消息队列里面发送一些内容,我们来测试几个消息,第一个单播下的点对点消息,

我们将一个消息发送队列里面,我们可以使用rabbitTemplate,两个方法,send传入exchange,就是交换器,

消息先给交换器,根据路由件放到哪个队列里面,后面这个就是我们要发的消息,这个是需要我们自己来

编辑这个消息,第一个传一个交换器,第二个是传一个路由件routekey,第三个传一个message,他的好处呢就是,

特别是message需要自己定义,Message需要自己定义,需要自己构造一个,自己构造的时候呢,我们来看一下这个

Message,我们要发的消息内容,amqp这个核心包里面

org.springframework.amqp.core.Message

你要把你发的消息序列号成一个数组,还有你消息的头信息,

public Message(byte[] body, MessageProperties messageProperties) { //NOSONAR
	this.body = body; //NOSONAR
	this.messageProperties = messageProperties;
}

可以定制消息体内容和消息头,这是rabbitTemplate.send,我们常见的是convertAndSend,转化并发送,我们只需要

传入交换器,路由件,还有数据对象,这个对象默认就会被当成消息体,而且会自动转换,我们只需要传入要发送的对象,

只需要传入发送的对象,自动序列化给rabbitmq,这个object是被当成消息体的,默认当成消息体,我们就可以用这个方法,

我们就直接用下面这个方法,我们要发点对点消息,我们就要按照交换器的规则,direct能够直接派发给一个队列,我们就连上

这个交换器,我们就把消息给exchange.direct,我们连上这个交换器,这个交换器我们绑定了很多的路由件,比如china,

路由件是他,交给这个队列,我们就用china.news,我们用这个路由件,要发送的内容是什么,就随便写一个map,我可以写一个

map,;里面存一些数据放进去,比如msg存一些提示消息,这是我们要发的数据,我来发送测试一下,我们看这个数据能不能到达,

这个运行完成了,我们来我们的消息队列来看一下

localhost:15672

我们刚才发的是china.news

SpringBoot高级-消息-RabbitTemplate发送接受消息&序列化机制_第1张图片

消息是这个样子的

SpringBoot高级-消息-RabbitTemplate发送接受消息&序列化机制_第2张图片

是因为序列号默认是JAVA的序列化方式,给我们序列化的对象,解码以后的样子,对象呗默认序列化以后,

发送出去,发送点对点消息,我们要接收到这个消息,怎么办呢,可以用这个API,我们同样用rabbitTemplate,

有这个receive方法可以接收,包括还有receiveAndConverter,接收并转化,如果用这个接收,你收来自哪个消息

队列的消息,会给你把这个消息转成Messager,里面就只有消息头和消息体,如果用receiveAndConverter,可以把消息

体转成我们想要的对象,我们最终的消息是发到china.news,这个里面了,所以我们要收数据,也是收这个消息队列的,

在这里写上消息队列的名称,然后把收过来的数据拿过来,这个数据的类型是什么,再来打印的数据是什么
package com.learn.springboot;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootAmqpApplicationTests {
	
	@Autowired
	RabbitTemplate rabbitTemplate;
	
	/**
	 * 1.单播(点对点)
	 */
	@Test
	public void contextLoads() {
		// Message需要自己构造一个,定义消息体内容和消息头
//		rabbitTemplate.send(exchange, routingKey, message);
		
		// object默认当成消息体,只需要传入要发送的对象,自动序列化发送给rabbitmq
//		rabbitTemplate.convertAndSend(routingKey, message, messagePostProcessor);
		
		Map map = new HashMap();
		map.put("msg", "这是第一个消息");
		map.put("data", Arrays.asList("helloworld",1231,true));		
		// 对象被默认序列化以后发送出去
		rabbitTemplate.convertAndSend("exchange.direct", "china.news",map);
	}
	
	@Test
	public void receive() {
		Object o = rabbitTemplate.receiveAndConvert("china.news");
		System.out.println(o.getClass());
		System.out.println(o);
	}
}
class java.util.HashMap
{msg=这是第一个消息, data=[helloworld, 1231, true]}
数据收到的类型是HashMap,拿到这些数据没什么问题,所以我们也能准确的收到数据,但是这个数据收到以后,

来看这个消息队列里面,这个消息队列就没有了

SpringBoot高级-消息-RabbitTemplate发送接受消息&序列化机制_第3张图片

确实已经没有了,刚才已经收到了,接收数据,有时候我想把数据序列化成JSON的格式,那种序列化的结果有点不好看,

如何将数据自动的转为JSON发送出去,其实非常的简单,我们到RabbitTemplate这里来看,为什么刚才是那种序列化结果,

原因我们来往下翻,原因有一个消息转换器

private volatile MessageConverter messageConverter = new SimpleMessageConverter();

这个转换器呢,默认是SimpleMessageConverter,这个和MessageConverter呢,我们来序列化数据的时候,就是按照JDK的

序列化规则,

content = SerializationUtils.deserialize(
	createObjectInputStream(new ByteArrayInputStream(message.getBody()), this.codebaseUrl));

我们可以给他换一个MessageConverter

org.springframework.amqp.support.converter.MessageConverter

我们来写上配置,比如我们在config包下写一个MyAMQPConfig,这个Config我们来写一个@Configuration,

我们就自己来放一个MessageConverter,这是我们的MessageConverter,来放哪个MessageConverter呢,就来看他有哪些,

抽象AbstractJsonMessageConverter有跟JSON有关的,Jackson2JsonMessageConverter,我们换成他,我们自动配置也会生效,

当我们来配置RabbitTemplate的时候,如果我们有自己定义的MessageConverter,也会给我们设置进来,

@Bean
@ConditionalOnSingleCandidate(ConnectionFactory.class)
@ConditionalOnMissingBean(RabbitTemplate.class)
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
	RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
	MessageConverter messageConverter = this.messageConverter.getIfUnique();
	if (messageConverter != null) {
		rabbitTemplate.setMessageConverter(messageConverter);
	}

我们再来测试这个消息,我们把消息发出去,来看还是不是原来的消息
package com.learn.config;

import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class MyAMQPConfig {

	@Bean
	public MessageConverter messageConvert() {
		return new Jackson2JsonMessageConverter();
	}
}

SpringBoot高级-消息-RabbitTemplate发送接受消息&序列化机制_第4张图片

消息就是我们的JSON数据,而且还有定义的消息头,消息头有我们key的类型,我们整个消息的类型,

application/json,序列化能过去,存起来,能不能取出消息,我们看到这块也是没有问题的,

class java.util.HashMap
{msg=这是第一个消息, data=[helloworld, 1231, true]}

比如我们发送Book对象,接下来我们来发一个图书的对象,我们来new一个book
package com.learn.bean;

public class Book {

	private String bookName;
	
	private String author;
	
	public Book() {
		super();
	}

	public Book(String bookName, String author) {
		super();
		this.bookName = bookName;
		this.author = author;
	}

	public String getBookName() {
		return bookName;
	}

	public void setBookName(String bookName) {
		this.bookName = bookName;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	@Override
	public String toString() {
		return "Book [bookName=" + bookName + ", author=" + author + "]";
	}
	
}
package com.learn.springboot;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.learn.bean.Book;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootAmqpApplicationTests {
	
	@Autowired
	RabbitTemplate rabbitTemplate;
	
	/**
	 * 1.单播(点对点)
	 */
	@Test
	public void contextLoads() {
		// Message需要自己构造一个,定义消息体内容和消息头
//		rabbitTemplate.send(exchange, routingKey, message);
		
		// object默认当成消息体,只需要传入要发送的对象,自动序列化发送给rabbitmq
//		rabbitTemplate.convertAndSend(routingKey, message, messagePostProcessor);
		
		Map map = new HashMap();
		map.put("msg", "这是第一个消息");
		map.put("data", Arrays.asList("helloworld",1231,true));		
		// 对象被默认序列化以后发送出去
		rabbitTemplate.convertAndSend("exchange.direct", "china.news",new Book("西游记","吴承恩"));
	}
	
	@Test
	public void receive() {
		Object o = rabbitTemplate.receiveAndConvert("china.news");
		System.out.println(o.getClass());
		System.out.println(o);
	}
}

SpringBoot高级-消息-RabbitTemplate发送接受消息&序列化机制_第5张图片

能不能反序列化获取出来呢,我们先用Object对象来接收,

class com.learn.bean.Book
Book [bookName=西游记, author=吴承恩]

这个Class拿到的是Book对象,强转也可以在这里强转,消息序列化和反序列化,我们测试的是一个单播,那如果想

广播,不管是单播还是广播,我们发给对应的exchange,我们再来测试一下,广播类型的转换器,然后路由件就不用写了,

因为广播是不用管路由件的,我们来测试一下广播
	/**
	 * 广播
	 */
	@Test
	public void sendMsg() {
		rabbitTemplate.convertAndSend("exchange.fanout","",new Book("三国演义","罗贯中"));
	}

SpringBoot高级-消息-RabbitTemplate发送接受消息&序列化机制_第6张图片

 

你可能感兴趣的:(SpringBoot)