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
等都採用類似的自動配置方式,大家可以採用本文類似的步驟閱讀相關原始碼。
本篇文章就到這裡了,希望大家身體健康,工作順利!