SpringBoot中Rabbit的基本使用和自动配置原理
1 一个简单的示例
在Spring Boot项目中使用spring-rabbit
时,需要经过以下几个步骤:
- 引入依赖。
- 配置基本连接信息。
- 创建消息发布者,并发送消息。
- 创建消息消费者,监听消息并处理。
我们以一个简单的例子开始,展示这个基本过程。
1.1 引入依赖
如果是Maven项目,需要在pom.xml
文件中引入基本依赖如下:
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.3.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.5.5</version>
</dependency>
其中:
spring-rabbit
用于与RabbitMQ服务器交互的工具包spring-boot-autoconfigure
用于自动配置RabbitMQ客户端与服务器连接等基本信息。
1.2 配置连接信息
由于spring-boot-autoconfigure
的自动配置功能,我们仅需要在application.yml
文件中配置连接信息即可。以下是一个例子:
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
其中:
host
:服务器地址。port
:服务器端口。username
:用户名。password
:密码。virtual-host
:交换机/队列所属虚拟主机。
1.3 消息发布者&消费者
我们直接在Spring Boot主程序中简单编写一个发布&接收消息的示例:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public Queue myQueue() {
return new Queue("myQueue");
}
@RabbitListener(queues = "myQueue")
public void listen(String in) {
System.out.println(in);
}
@Bean
public ApplicationRunner runner(AmqpTemplate template) {
return args -> template.convertAndSend("myQueue", "Hello World!");
}
}
我们在这段代码中做了如下工作:
- 声明队列
myQueue
。 - 创建消息消费者,监听队列
myQueue
。 - 使用
AmqpTemplate
对象,向消息队列myQueue
发送消息:Hello World!
。
1.4 启动项目
如果我们在本地启动了RabbitMQ服务器,并且端口、用户名和密码都没有问题。
那么启动项目,可以从控制台得到如下输出:
Hello World!
1.5 提出问题
不知道大家会不会有这些疑问:
- 为什么在
application.yml
文件中写入这些字符就可以连接到RabbitMQ服务器? AmqpTemplate
对象为什么不用声明就可以直接使用?
其实,这一切的功劳都归因于我们引入了spring-boot-autoconfigure
。它为我们做了以下基本工作:
- 从
application.yml
文件中读取基本配置信息。 - 使用基本配置信息为我们创建出
AmqpTemplate
等对象,存放到Spring容器中。
接下来,由我来给大家揭开spring-boot-autoconfigure
为spring-rabbit
自动配置的面纱。
2 RabbitProperties
2.1 看看源码
在spring-boot-autoconfigure
依赖的org.springframework.boot.autoconfigure.amqp
包下,有个RabbitProperties
类。
它的作用是:从application.yml
文件中读取到spring-rabbit
相关配置信息。
在IDEA中,我们可以简单使用以下方法进入到这个类。
方法一,从application.yml
文件进入:
- 在
application.yml
文件中,按住Ctrl
键,鼠标左键点击某个配置信息。
方法二,搜索:
- 快速连续按两下
Shift
键,跳出搜索框进行搜索。
RabbitProperties
源码简要如下:
package org.springframework.boot.autoconfigure.amqp;
@ConfigurationProperties(prefix = "spring.rabbitmq")
public class RabbitProperties {
private static final int DEFAULT_PORT = 5672;
private static final int DEFAULT_PORT_SECURE = 5671;
private String host = "localhost";
private Integer port;
private String username = "guest";
private String password = "guest";
private final Ssl ssl = new Ssl();
private String virtualHost;
private String addresses;
private AddressShuffleMode addressShuffleMode = AddressShuffleMode.NONE;
@DurationUnit(ChronoUnit.SECONDS)
private Duration requestedHeartbeat;
private int requestedChannelMax = 2047;
private boolean publisherReturns;
private ConfirmType publisherConfirmType;
private Duration connectionTimeout;
private Duration channelRpcTimeout = Duration.ofMinutes(10);
private final Cache cache = new Cache();
private final Listener listener = new Listener();
private final Template template = new Template();
private List<Address> parsedAddresses;
}
2.2 功能讲解
通过简单阅读RabbitProperties
,我们可以发现两点重要信息:
- 该类添加了
@ConfigurationProperties(prefix = "spring.rabbitmq")
注解。 - 该类的部分成员变量名与
application.yml
中配置信息名一致,例如host
、port
、username
、password
和virtual-host
。
在此我们需要先简单了解@ConfigurationProperties
注解的功能:
@ConfigurationProperties
可以用来获取外部配置信息,默认是application.yml
等Spring Boot配置文件。- 将该注解添加到类上,会通过
setter
(默认)或constructor
方法的方式,将外部配置信息赋值给对应成员变量。 prefix
可以指定配置文件中的前缀,用来将外部配置信息与成员变量进行匹配。
回到RabbitProperties
源码,我们应该很容易理解RabbitProperties
的功能:
- 从
application.yml
文件中读取前缀为spring.rabbitmq
的配置信息。 - 将
setter
方法将配置信息赋值给对应的成员变量。
通过上述过程,完成了将配置信息从文件读取到缓存(RabbitProperties
对象)的过程,以便于后续使用。
2.3 动手实战
我们也可以编写一个类似的MyRabbitProperties
,用来从application.yml
文件中读取RabbitMQ配置信息。
2.3.1 MyRabbitProperties
代码如下:
@ConfigurationProperties(prefix = "spring.rabbitmq")
public class MyRabbitProperties {
private String host;
private Integer port;
private String username;
private String password;
private String virtualHost;
public void setHost(String host) {
this.host = host;
}
public void setPort(Integer port) {
this.port = port;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setVirtualHost(String virtualHost) {
this.virtualHost = virtualHost;
}
@Override
public String toString() {
return "MyRabbitProperties{" +
"host='" + host + '\'' +
", port=" + port +
", username='" + username + '\'' +
", password='" + password + '\'' +
", virtualHost='" + virtualHost + '\'' +
'}';
}
}
简要说明:
- 添加
@ConfigurationProperties(prefix = "spring.rabbitmq")
,用来从application.yml
文件中读取前缀为spring.rabbitmq
的配置信息。 - 添加
setter
方法,用来为成员变量注入配置信息。 - 添加
toString()
方法,便于后续打印信息。
2.3.2 application.yml
我们在application.yml
文件中写入如下配置信息:
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
2.3.3 启动类
我们简单编写如下启动类:
@EnableConfigurationProperties(MyRabbitProperties.class)
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public ApplicationRunner runner(MyRabbitProperties properties) {
return args -> {
System.out.println(properties);
};
}
}
简要说明:
- 添加
SpringBootApplication
注解,表明这是一个Spring Boot启动类。 - 添加
@EnableConfigurationProperties(MyRabbitProperties.class)
注解,可以将@ConfigurationProperties
标注的类声明为Bean
。这样Spring容器才能为MyRabbitProperties
注入配置信息。 - 添加
main()
函数,用来启动Spring Boot项目。 - 声明
ApplicationRunner
为Bean
,项目启动后会执行其中的代码。
需要注意的是:联合@EnableConfigurationProperties
只是@ConfigurationProperties
注解使用方式的一种。我们也可以直接在MyRabbitProperties
类上标注@Configuration
或@Component
等注解,直接声明为Bean
。
2.3.4 启动项目
启动项目后,可以从控制台中得到如下输出,说明我们成功将配置信息注入到MyRabbitProperteis
对象中:
MyRabbitProperties{host='localhost', port=5672, username='guest', password='guest', virtualHost='/'}
3 RabbitAutoConfiguration
通过RabbitProperteis
我们已经从application.yml
文件中获取到了连接RabbitMQ服务器的配置信息,接下来我们继续揭秘:
spring-boot-autoconfigure
为我们预先创建了哪些Bean
?- 它是如何创建这些
Bean
的?
预先小结:这些功能都在RabbitAutoConfiguration
中。
3.1 看看源码
在spring-boot-autoconfigure
依赖的org.springframework.boot.autoconfigure.amqp
包下,有个RabbitAutoConfiguration
类。
它的作用是,当类路径中存在RabbitMQ和Spring AMQP客户端类库时,可能会为我们自动创建如下Bean
:
org.springframework.amqp.rabbit.connection.CachingConnectionFactory
:创建客户端与RabbitMQ服务器连接的工厂。org.springframework.amqp.core.AmqpAdmin
:封装了声明交换机/消息队列/绑定等模板方法。org.springframework.amqp.rabbit.core.RabbitTemplate
:封装了与RabbitMQ服务器交互的模板方法,例如:发送消息和接收消息等。org.springframework.amqp.rabbit.core.RabbitMessagingTemplate
:功能与RabbitTemplate
相同,但底层使用org.springframework.messaging.Message
作为消息抽象,较少使用。
在IDEA中,我们可以简单使用以下方法进入到这个类:
- 快速连续按两下
Shift
键,跳出搜索框进行搜索。
其源码结构如下:
package org.springframework.boot.autoconfigure.amqp;
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {
// 创建连接工厂Bean
protected static class RabbitConnectionFactoryCreator {}
// 创建RabbitTemplate和AmqpAdmin
protected static class RabbitTemplateConfiguration {}
// 创建RabbitMessagingTemplate
protected static class MessagingTemplateConfiguration {}
}
3.2 功能讲解
通过简单阅读源码,我们可以将其分成四个部分进行介绍:
RabbitAutoConfiguration
标注注解:自动配置主类。RabbitConnectionFactoryCreator
内部类:创建连接工厂,默认为CachingConnectionFactory
。RabbitTemplateConfiguration
内部类:创建RabbitTemplate
和AmqpAdmin
。MessagingTemplateConfiguration
内部类:创建RabbitMessagingTemplate
。
接下来,我们分别来介绍它们的功能。
3.2.1 配置主类
RabbitAutoConfiguration
配置主类标注如以下四个注解:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
1、@Configuration(proxyBeanMethods = false)
@Configuration
将RabbitAutoConfiguration
标注成配置类,可以在其内部声明Bean
。
proxyBeanMethods = false
表示Spring容器不会动态代理内部用@Bean
标注的方法,可以提高性能。
2、@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@ConditionalOnClass
注解表示只有当类路径中存在以下类时,才会将RabbitAutoConfiguration
注册成Bean
:
org.springframework.amqp.rabbit.core.RabbitTemplate
com.rabbitmq.client.Channel
也就是说,只有在类路径中存在RabbitMQ和Spring AMQP客户端类库,Spring容器才会为我们对RabbitMQ进行自动配置。
4、@EnableConfigurationProperties(RabbitProperties.class)
@EnableConfigurationProperties
与@ConfigurationProperties
注解联用,可以将RabbitProperties
注册成Bean
,从而将配置信息从application.yml
读取到内存中。
5、@Import(RabbitAnnotationDrivenConfiguration.class)
@Import
注解可以引入另外的配置类——RabbitAnnotationDrivenConfiguration
:用于配置Spring AMQP注解驱动断点。
简单来说,它为我们注册了如下Bean
:
org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory
:创建SimpleMessageListenerContainer
的工厂。DirectRabbitListenerContainerFactory
:创建DirectMessageListenerContainer
的工厂。
这两个XxxListenerContainer
主要用来监听RabbitMQ服务器发送的消息。
因此,RabbitAnnotationDrivenConfiguration
配置类主要与监听消息有关。由于篇幅限制,这里就不进行深入讲解了。其源码结构如下:
package org.springframework.boot.autoconfigure.amqp;
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(EnableRabbit.class)
class RabbitAnnotationDrivenConfiguration {
@Bean
@ConditionalOnMissingBean
SimpleRabbitListenerContainerFactoryConfigurer simpleRabbitListenerContainerFactoryConfigurer() {}
@Bean(name = "rabbitListenerContainerFactory")
@ConditionalOnMissingBean(name = "rabbitListenerContainerFactory")
@ConditionalOnProperty(prefix = "spring.rabbitmq.listener", name = "type", havingValue = "simple",
matchIfMissing = true)
SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(}
@Bean
@ConditionalOnMissingBean
DirectRabbitListenerContainerFactoryConfigurer directRabbitListenerContainerFactoryConfigurer() {}
@Bean(name = "rabbitListenerContainerFactory")
@ConditionalOnMissingBean(name = "rabbitListenerContainerFactory")
@ConditionalOnProperty(prefix = "spring.rabbitmq.listener", name = "type", havingValue = "direct")
DirectRabbitListenerContainerFactory directRabbitListenerContainerFactory(}
@Configuration(proxyBeanMethods = false)
@EnableRabbit
@ConditionalOnMissingBean(name = RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
static class EnableRabbitConfiguration {}
}
3.2.2 RabbitConnectionFactoryCreator内部类
RabbitConnectionFactoryCreator
内部类的作用是:注册CachingConnectionFactory
作为连接工厂Bean
。
其头部标注了以下两个注解:
@Configuration(proxyBeanMethods = false)
:声明为配置类。@ConditionalOnMissingBean(ConnectionFactory.class)
:只有org.springframework.amqp.rabbit.connection.ConnectionFactory
类存在时才会生效,即只有类路径中添加了spring-rabbit
依赖时才会生效。
其内部默认将CachingConnectionFactory
注册为连接工厂Bean
,步骤如下:
- 实例化
CachingConnectionFactory
对象。 - 将配置文件中的配置信息注入到
CachingConnectionFactory
对象中。
其源码简要如下:
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(ConnectionFactory.class)
protected static class RabbitConnectionFactoryCreator {
// 注册CachingConnectionFactory作为连接工厂Bean
@Bean
public CachingConnectionFactory rabbitConnectionFactory(RabbitProperties properties,
ResourceLoader resourceLoader, ObjectProvider<CredentialsProvider> credentialsProvider,
ObjectProvider<CredentialsRefreshService> credentialsRefreshService,
ObjectProvider<ConnectionNameStrategy> connectionNameStrategy,
ObjectProvider<ConnectionFactoryCustomizer> connectionFactoryCustomizers) throws Exception {
// 1、实例化CachingConnectionFactory对象
com.rabbitmq.client.ConnectionFactory connectionFactory = getRabbitConnectionFactoryBean(properties,
resourceLoader, credentialsProvider, credentialsRefreshService).getObject();
connectionFactoryCustomizers.orderedStream()
.forEach((customizer) -> customizer.customize(connectionFactory));
CachingConnectionFactory factory = new CachingConnectionFactory(connectionFactory);
// 2、将配置文件中的配置信息注入到CachingConnectionFactory对象中
PropertyMapper map = PropertyMapper.get();
map.from(properties::determineAddresses).to(factory::setAddresses);
// 省略其他map.from().to()方法
return factory;
}
// 实例化RabbitConnectionFactoryBean对象
private RabbitConnectionFactoryBean getRabbitConnectionFactoryBean(RabbitProperties properties,
ResourceLoader resourceLoader, ObjectProvider<CredentialsProvider> credentialsProvider,
ObjectProvider<CredentialsRefreshService> credentialsRefreshService) {
// 省略
return factory;
}
}
3.2.3 RabbitTemplateConfiguration内部类
RabbitTemplateConfiguration
内部类的作用是:注册RabbitTemplate
和AmqpAdmin
作为交互模板Bean
。
其头部标注了以下两个注解:
@Configuration(proxyBeanMethods = false)
:声明为配置类。@Import(RabbitConnectionFactoryCreator.class)
:引入RabbitConnectionFactoryCreator
配置类。
其内部默认注册RabbitTemplate
和AmqpAdmin
作为交互模板Bean
,本质上就是实例化对象。源码简要如下:
@Configuration(proxyBeanMethods = false)
@Import(RabbitConnectionFactoryCreator.class)
protected static class RabbitTemplateConfiguration {
@Bean
@ConditionalOnMissingBean
public RabbitTemplateConfigurer rabbitTemplateConfigurer(RabbitProperties properties,
ObjectProvider<MessageConverter> messageConverter,
ObjectProvider<RabbitRetryTemplateCustomizer> retryTemplateCustomizers) {
RabbitTemplateConfigurer configurer = new RabbitTemplateConfigurer();
configurer.setMessageConverter(messageConverter.getIfUnique());
configurer
.setRetryTemplateCustomizers(retryTemplateCustomizers.orderedStream().collect(Collectors.toList()));
configurer.setRabbitProperties(properties);
return configurer;
}
// 注册RabbitTemplate
@Bean
@ConditionalOnSingleCandidate(ConnectionFactory.class)
@ConditionalOnMissingBean(RabbitOperations.class)
public RabbitTemplate rabbitTemplate(RabbitTemplateConfigurer configurer, ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate();
configurer.configure(template, connectionFactory);
return template;
}
// 注册AmqpAdmin
@Bean
@ConditionalOnSingleCandidate(ConnectionFactory.class)
@ConditionalOnProperty(prefix = "spring.rabbitmq", name = "dynamic", matchIfMissing = true)
@ConditionalOnMissingBean
public AmqpAdmin amqpAdmin(ConnectionFactory connectionFactory) {
return new RabbitAdmin(connectionFactory);
}
}
3.2.4 MessagingTemplateConfiguration内部类
MessagingTemplateConfiguration
内部类的作用是:注册RabbitMessagingTemplate
作为交互模板Bean
。
RabbitMessagingTemplate
与RabbitTemplate
的功能没有本质差别,它们的差别在于继承结构不同:
RabbitMessagingTemplate
:继承自org.springframework.messaging.core.AbstractMessageSendingTemplate
抽象类。RabbitTemplate
:继承自org.springframework.amqp.rabbit.connection.RabbitAccessor
抽象类。
项目中通常使用的是RabbitTemplate
。
MessagingTemplateConfiguration
内部类的源码如下:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RabbitMessagingTemplate.class)
@ConditionalOnMissingBean(RabbitMessagingTemplate.class)
@Import(RabbitTemplateConfiguration.class)
protected static class MessagingTemplateConfiguration {
@Bean
@ConditionalOnSingleCandidate(RabbitTemplate.class)
public RabbitMessagingTemplate rabbitMessagingTemplate(RabbitTemplate rabbitTemplate) {
return new RabbitMessagingTemplate(rabbitTemplate);
}
}
4 总结
通过以上的简单介绍,想必大家对Spring Boot项目中spring-rabbit
的自动配置有了大概的了解。
Spring Boot对其他工具,如:spring-web
、spring-security
、spring-datasource
、spring-transaction
、spring-kafka
以及spring.jackson
等都采用类似的自动配置方式,大家可以采用本文类似的步骤阅读相关源码。
本篇文章就到这里了,希望大家身体健康,工作顺利!