在RabbitAutoConfiguration
类可以看到其自动配置的实现,主要分为以下四步。
CachingConnectionFactory
连接工厂建立连接。@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());
}
...
RabbitProperties
类中,RabbitProperties实际上是绑定了application配置文件中以spring.rabbitmq
为前缀的配置。ConfigurationProperties(
prefix = "spring.rabbitmq"
)
public class RabbitProperties {
private String host = "localhost";
private int port = 5672;
...
RabbitTemplate
向RabbitMq发送或接受消息。@Bean
@ConditionalOnSingleCandidate(ConnectionFactory.class)
@ConditionalOnMissingBean({RabbitTemplate.class})
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
MessageConverter messageConverter = (MessageConverter)this.messageConverter.getIfUnique();
if (messageConverter != null) {
rabbitTemplate.setMessageConverter(messageConverter);
}
...
AmqpAdmin
系统管理功能组件,作用是创建和声明队列,交换器等等。@ConditionalOnMissingBean({AmqpAdmin.class})
public AmqpAdmin amqpAdmin(ConnectionFactory connectionFactory) {
return new RabbitAdmin(connectionFactory);
}
...
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
spring.rabbitmq.host=xxx
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#spring.rabbitmq.virtual-host=可以不填,默认为‘/’
RabbitTemplate.send
方法发送。(不常用)public void send(final String exchange, final String routingKey, final Message message, final CorrelationData correlationData) throws AmqpException {
this.execute(new ChannelCallback<Object>() {
public Object doInRabbit(Channel channel) throws Exception {
RabbitTemplate.this.doSend(channel, exchange, routingKey, message, RabbitTemplate.this.returnCallback != null && (Boolean)RabbitTemplate.this.mandatoryExpression.getValue(RabbitTemplate.this.evaluationContext, message, Boolean.class), correlationData);
return null;
}
}, this.obtainTargetConnectionFactory(this.sendConnectionFactorySelectorExpression, message));
}
其中exchange代表交换器,routingKey为路由表,message需要自己构建,是可自定义的消息体内容和消息头。
public Message(byte[] body, MessageProperties messageProperties) {
this.body = body;
this.messageProperties = messageProperties;
}
RabbitTemplate.convertAndSend
方法发送。(常用)public void convertAndSend(String exchange, String routingKey, Object object, CorrelationData correlationData) throws AmqpException {
this.send(exchange, routingKey, this.convertMessageIfNecessary(object), correlationData);
}
object是要发送的对象,RabbitTemplate会将它以默认序列化方式序列化后发送给rabbitmq(默认为SimpleMessageConverter
)。本质上与send方法是一致的,只不过调用了this.convertMessageIfNecessary
方法对这个object
进行转换。
示例
下面是一个单播的测试方法,通过构造一个map对象来存放需要发送的信息,之后使用convertAndSend
发送给RabbitMq中匹配路由键的队列。
/**
* 1.单播(点对点)
*/
@Test
public void sendMessage01() {
Map<String,Object> map = new HashMap<>();
map.put("msg", "这是第一个信息");
map.put("data", Arrays.asList("HelloWorld", 123, true));
rabbitTemplate.convertAndSend("exchange.direct","atguigu.news",map);
}
登录到RabbitMq后台,在绑定了atguigu.news路由键的队列中可以看到序列化后的数据信息。数据信息之所以呈现这种格式是因为使用的是RabbitTemplate
默认的序列化方法。
如果想将数据信息序列化成我们可读的,比如json数据。可以将默认的MessageConverter
(消息转换器)切换成jackson2JsonMessageConverter
来将数据序列化成json格式。
@Configuration
public class RabbitmqConfig {
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
}
再次发送消息,可以看到在切换消息转换器后,数据已经被序列化为json格式。
RabbitTemplate.receiveAndConvert
方法获取指定队列中的消息。public Object receiveAndConvert(String queueName, long timeoutMillis) throws AmqpException {
Message response = timeoutMillis == 0L ? this.doReceiveNoWait(queueName) : this.receive(queueName, timeoutMillis);
return response != null ? this.getRequiredMessageConverter().fromMessage(response) : null;
}
示例
取出上一步发送至RabbitMq队列中的数据信息。
@Autowired
RabbitTemplate rabbitTemplate;
@Test
public void getMessage(){
Object o = rabbitTemplate.receiveAndConvert("atguigu.news");
System.out.println(o);
}
可以看到在控制台正常打印了从队列中取得的信息,注:取得的队列中最早接收到的消息。
@EnableRabbit
@SpringBootApplication
public class SpringBootRabbitmqTestApplication {
...
@RabbitListener
注解监听指定队列,获取我们需要的消息。@Service
public class BookService {
@RabbitListener(queues = "队列名")
public void receive01(Object object){
System.out.println(object);
}
@RabbitListener(queues = "atguigu")
public void receive02(Message message){
System.out.println(message.getBody());
System.out.println(message.getMessageProperties());
}
}
对于一些特殊的需求,message
可以用来获得消息头,使用getBody
和getMessageProperties
来获得消息内容和消息头信息。类似下图所示:
在AmqpAdmin类中封装了许多操作交换器,队列的方法。
public interface AmqpAdmin {
void declareExchange(Exchange var1);
boolean deleteExchange(String var1);
Queue declareQueue();
String declareQueue(Queue var1);
boolean deleteQueue(String var1);
void deleteQueue(String var1, boolean var2, boolean var3);
void purgeQueue(String var1, boolean var2);
void declareBinding(Binding var1);
void removeBinding(Binding var1);
Properties getQueueProperties(String var1);
}
示例
创建一个amqpadmin.exchange
交换器,再创建一个amqpadmin.queue
队列,最后我们将他们用amqp.haha
路由键绑定起来,就实现了一个完整消息队列的创建。
@Test
public void createExchange(){
amqpAdmin.declareExchange(new DirectExchange("amqpadmin.exchange"));
amqpAdmin.declareQueue(new Queue("amqpadmin.queue"));
amqpAdmin.declareBinding(new Binding("amqpadmin.queue", Binding.DestinationType.QUEUE,"amqpadmin.exchange","amqp.haha",null));
}
以下是对Binding参数的分析。我们可以看到第一个参数是目标队列或交换器,第二个参数是目标类型,如果为队列则为Binding.DestinationType.QUEUE
,第三个参数是交换器名,第四个参数为参数:
public Binding(String destination, Binding.DestinationType destinationType, String exchange, String routingKey, Map<String, Object> arguments) {
this.destination = destination;
this.destinationType = destinationType;
this.exchange = exchange;
this.routingKey = routingKey;
this.arguments = arguments;
}
运行测试方法,登录RabbitMq后台可以看到我们创建的队列,交换器和路由键。