1. Overview
In this article Spring Boot series 13 Spring boot integration RABBITMQ, we describe how to use RABBITMQ in spring boot, and in this article we analyze how Spring boot integrates rabbitmq from the source. 2. Entrance
The spring.factories in Spring-boot-autoconfigure.jar has the following definition, indicating that when spring starts, rabbitautoconfiguration initialization is performed
..... # Auto Configure
org.springframework.boot.autoconfigure.enableautoconfiguration=\
Org.springframework.boot.autoconfigure.amqp.rabbitautoconfiguration,\ ...
3. Rabbitproperties
APPLICATION_*.YML Properties File
Spring:
# config rabbitmqspring:
rabbitmq:
host:10.240.80.134
username:spring-boot
Password: Spring-boot
Virtual-host:spring-boot-vhost
The above properties file will be injected into the Rabbitproperties property
@ConfigurationProperties (prefix = "SPRING.RABBITMQ") public
class Rabbitproperties {
...
}
4. Rabbitautoconfiguration
4.1. Annotation analysis on the class:
This is a configuration class that initializes the above mentioned Rabbitproperties object at startup, and then it introduces another configuration class Rabbitannotationdrivenconfiguration, this configuration class and message listener about us later
This class has 3 inner classes, all of which are configuration classes, which are the classes required to initialize RABBITMQ based on the condition
@Configuration
@ConditionalOnClass ({rabbittemplate.class, channel.class})
//initializes Rabbitproperties.class
@EnableConfigurationProperties (Rabbitproperties.class)
// Introduction of @configuration class Rabbitannotationdrivenconfiguration
@Import (rabbitannotationdrivenconfiguration.class) Public
class Rabbitautoconfiguration {
...
}
4.2. Internal class Rabbitconnectionfactorycreator
The
Inner class Rabbitconnectionfactorycreator Initializes the Cachingconnectionfactory instance based on the parameters configured by Rabbitproperties (it is connectionfactory Subclass), this instance is the connection pool that connects RABBITMQ. The
Cachingconnectionfactory instance is an encapsulation of com.rabbitmq.client.ConnectionFactory and Com.rabbitmq.client.Channel to RABBITMQ, which caches both Source. Cachingconnectionfactory has two cache modes,
1. If you choose Cachemode#channel Cache mode, when we call the CreateConnection () method, we return the same connection each time. By default, only one connection is created, and only one channel is created (by configuring the channel quantity parameter, you can create a cache of multiple channel). That is, you can create multiple channel, but all channel share the same connection
2. If you select Cachemode#connection cache mode, you can configure both the amount of connection created and the channel data. When CreateConnection () is called, the available connection are obtained from the cache, and a new connection is created if none is reached and the number created does not reach the upper limit. Empathy channel
@Configuration @ConditionalOnMissingBean (connectionfactory.class) protected static class Rabbitconnectionfactorycreator {@Bean public cachingconnectionfactory rabbitconnectionfactory (RabbitProperties C Onfig) throws Exception {//rabbitproperties configuration RABBITMQ Connection factory class Rabbitconnectionfactorybean facto
ry = new Rabbitconnectionfactorybean ();
if (config.determinehost () = null) {Factory.sethost (Config.determinehost ());
} ... factory.afterpropertiesset (); Connection Cache class Cachingconnectionfactory connectionfactory = new Cachingconnectionfactory (Factory.getobject (
));
Connectionfactory.setaddresses (Config.determineaddresses ());
Connectionfactory.setpublisherconfirms (Config.ispublisherconfirms ());
Connectionfactory.setpublisherreturns (Config.ispublisherreturns ());
... return connectionfactory; }
}
4.3. Internal class rabbittemplateconfiguration
The inner class Rabbittemplateconfiguration assigns rabbitproperties configured parameters, Messageconverter to the corresponding member variables of the class through the constructor of the class, and then in method Rabbittemplate ( Rabbittemplate and Rabbitadmin are created based on Cachingconnectionfactory instances created by Rabbitconnectionfactorycreator.
@Configuration//introduction of Rabbitconnectionfactorycreator @Import (rabbitconnectionfactorycreator.class) protected static
Class Rabbittemplateconfiguration {private final objectprovider<messageconverter> messageconverter;
Private final rabbitproperties properties; Inject Messageconverter and rabbitproperties public rabbittemplateconfiguration (Objectprovider<messageconverte
R> Messageconverter, Rabbitproperties properties) {this.messageconverter = Messageconverter;
This.properties = properties; }//Initialize Rabbittemplate @Bean @ConditionalOnSingleCandidate (connectionfactory.class) @ConditionalOnMissin GBean (rabbittemplate.class) public rabbittemplate rabbittemplate (connectionfactory connectionfactory) {//Create R
Abbittemplate rabbittemplate rabbittemplate = new Rabbittemplate (connectionfactory);
Messageconverter messageconverter = This.messageConverter.getIfUnique (); if (MessageconverteR! = null) {//config Messageconverter rabbittemplate.setmessageconverter (messageconverter);
}//Other parameter configuration slightly ... return rabbittemplate; }//Initialize Amqpadmin @Bean @ConditionalOnSingleCandidate (connectionfactory.class) @ConditionalOnProperty (pr Efix = "SPRING.RABBITMQ", name = "Dynamic", matchifmissing = True) @ConditionalOnMissingBean (amqpadmin.class) Publ IC amqpadmin amqpadmin (connectionfactory connectionfactory) {//Create rabbitadmin return new Rabbitadmin (conn
Ectionfactory);
}
}
4.4. Internal configuration class: Messagingtemplateconfiguration
Internal Configuration class: Messagingtemplateconfiguration
The Rabbittemplate instance created above is injected and created through the Rabbitmessagingtemplate () method Rabbitmessagingtempla
@Configuration
@ConditionalOnClass (rabbitmessagingtemplate.class)
@ConditionalOnMissingBean ( Rabbitmessagingtemplate.class)
//Introduce the Rabbittemplateconfiguration configuration class
@Import ( Rabbittemplateconfiguration.class)
protected static class Messagingtemplateconfiguration {
// Generates an instance of Rabbitmessagingtemplate, where Rabbittemplate is instantiated by rabbittemplateconfiguration
@Bean
@ Conditionalonsinglecandidate (rabbittemplate.class) public
rabbitmessagingtemplate Rabbitmessagingtemplate (
rabbittemplate rabbittemplate) {
return new rabbitmessagingtemplate (rabbittemplate);
}
}
The RABBITMQ sender-related bean initialization is done with the above configuration, and we can send messages using Rabbittemplate and rabbitadmin. This configuration is more complex 5 if you want to listen for RABBITMQ messages as well . Rabbitannotationdrivenconfiguration
This class is introduced in this class of rabbitautoconfiguration, which creates a bean that listens for message-related messages. Let's analyze this class in detail. 5.1. How to construct a class:
Incoming monitoring requires Messageconverter instances, Messagerecoverer instances, rabbitproperties instances, member variables of the class as
@Configuration
@ConditionalOnClass (enablerabbit.class)
class Rabbitannotationdrivenconfiguration {
Private final objectprovider<messageconverter> Messageconverter;
Private final objectprovider<messagerecoverer> Messagerecoverer;
Private final rabbitproperties properties;
Rabbitannotationdrivenconfiguration (objectprovider<messageconverter> messageconverter,
ObjectProvider <MessageRecoverer> messagerecoverer,
rabbitproperties properties) {
This.messageconverter = Messageconverter;
This.messagerecoverer = Messagerecoverer;
This.properties = properties;
}
...
5.2. Rabbitlistenercontainerfactoryconfigurer () Method of class
Creates a Simplerabbitlistenercontainerfactoryconfigurer object that holds the Messageconverter instance required to create the Rabbitlistenercontainer, Messagerecoverer instances, Rabbitproperties instances
Instance Simplerabbitlistenercontainerfactoryconfigurer object, setting properties for Messageconverter, Messagerecovere, RABBITMQ
@Bean
@ConditionalOnMissingBean Public
simplerabbitlistenercontainerfactoryconfigurer Rabbitlistenercontainerfactoryconfigurer () {
Simplerabbitlistenercontainerfactoryconfigurer configurer = new Simplerabbitlistenercontainerfactoryconfigurer ();
Configurer.setmessageconverter (This.messageConverter.getIfUnique ());
Configurer.setmessagerecoverer (This.messageRecoverer.getIfUnique ());
Configurer.setrabbitproperties (this.properties);
return configurer;
}
5.3. Rabbitlistenercontainerfactory () method in class
Create Instance Simplerabbitlistenercontainerfactory (is a subclass of Rabbitlistenercontainerfactory), Where Simplerabbitlistenercontainerfactoryconfigurer comes from the following method, ConnectionFactory from Rabbitautoconfiguration, which has been explained above
@Bean
@ConditionalOnMissingBean (name = "Rabbitlistenercontainerfactory")
public Simplerabbitlistenercontainerfactory Rabbitlistenercontainerfactory (
Simplerabbitlistenercontainerfactoryconfigurer Configurer,
connectionfactory connectionfactory) {
Simplerabbitlistenercontainerfactory factory = new Simplerabbitlistenercontainerfactory ();
Configurer.configure (Factory, connectionfactory);
return factory;
}
5.4. Start @enablerabbit
This inner class mainly looks at his @enablerabbit annotations, which use the Rabbitlistenercontainer parameter and create other related bean instances and listen for messages. The next section details @enablerabbit
@EnableRabbit
@ConditionalOnMissingBean (name = Rabbitlistenerconfigutils.rabbit_listener_annotation_ Processor_bean_name)
protected static class Enablerabbitconfiguration {
}
6. @EnableRabbit
Introducing the Configuration Class Rabbitbootstrapconfiguration
@Target (Elementtype.type)
@Retention (retentionpolicy.runtime)
@Documented
// Introduces the configuration class Rabbitbootstrapconfiguration
@Import (rabbitbootstrapconfiguration.class) public
@interface enablerabbit {
}
7. Rabbitbootstrapconfiguration
In this configuration class, create Rabbitlistenerannotationbeanpostprocessor and Rabbitlistenerendpointregistry.
@Configuration public
class Rabbitbootstrapconfiguration {
//create Rabbitlistenerannotationbeanpostprocessor, @RabbitListener + @RabbitHandler Annotated method when you receive a listener message that is distributed to these methods for processing
@Bean (name = Rabbitlistenerconfigutils.rabbit_ Listener_annotation_processor_bean_name)
@Role (beandefinition.role_infrastructure)
public Rabbitlistenerannotationbeanpostprocessor rabbitlistenerannotationprocessor () {
return new Rabbitlistenerannotationbeanpostprocessor ();
}
Create a rabbitlistenerendpointregistry for the Listener node's registration
@Bean (name = Rabbitlistenerconfigutils.rabbit_listener_endpoint_ registry_bean_name) Public
rabbitlistenerendpointregistry Defaultrabbitlistenerendpointregistry () {
return new Rabbitlistenerendpointregistry ();
}
}
8. Rabbitlistenerannotationbeanpostprocessor
Inheriting Beanpostprocessor, after spring creates the object, it intercepts all method 8.1 of the @rabbitlistener+ @RabbitHandler annotations . The aftersingletonsinstantiated () method of the class
When the class is instantiated, initialization is performed and important operations
1. Set the Get Rabbitlistenerendpointregistry instance and set the instance to Rabbitlistenerendpointregistrar
2. Setting the Containerfactorybeanname name in Rabbitlistenerendpointregistrar is called Rabbitlistenercontainerfactory
3. Call Rabbitlistenerendpointregistrar.afterpropertiesset () for initialization, which is described later in this method
Create Instance private final Rabbitlistenerendpointregistrar registrar = new Rabbitlistenerendpointregistrar (); @Override public void aftersingletonsinstantiated () {...//Set get Rabbitlistenerendpointregistry instance and set instance to Rabbitliste
Nerendpointregistrar if (this.registrar.getEndpointRegistry () = = null) {if (this.endpointregistry = = null) { Assert.state (this.beanfactory! = NULL, "Beanfactory must is set to find endpoint registry by
Bean name "); This.endpointregistry = This.beanFactory.getBean (Rabbitlistenerconfigutils.rabbit_listener_endpoint_regis
Try_bean_name, Rabbitlistenerendpointregistry.class);
} this.registrar.setEndpointRegistry (This.endpointregistry); }//Set containerfactorybeanname name in Rabbitlistenerendpointregistrar called rabbitlistenercontainerfactory if (This.contai Nerfactorybeanname = null) {This.registrar.setContainerFactoryBeanName (This.containerfactorybeannaME); }//Set The custom handler method factory once resolved by the Configurer messagehandlermethodfactory Handlermet
Hodfactory = This.registrar.getMessageHandlerMethodFactory (); if (handlermethodfactory! = null) {this.messageHandlerMethodFactory.setMessageHandlerMethodFactory (handlermethodf
Actory);
}//actually register all listeners, initialize Rabbitlistenerendpointregistrar this.registrar.afterPropertiesSet ();
}
8.2. Postprocessafterinitialization () method
The Postprocessafterinitialization () method is executed after the object is initialized, and this method intercepts all the methods that are @rabbitlistener and @rabbithandler annotations.
1. @RabbitListener If the annotation is on a method, the method Processamqplistener () is called, and the method is called using the Methodrabbitlistenerendpoint encapsulation
2. @RabbitListener If the annotation is on a class and the class has a method @rabbithandler annotation, call Processmultimethodlisteners (), The method is invoked using the Multimethodrabbitlistenerendpoint encapsulation
Methodrabbitlistenerendpoint and Multimethodrabbitlistenerendpoint are Methodrabbitlistenerendpoint's subclasses.
@Override public Object Postprocessafterinitialization (final Object Bean, final String beanname) throws Beansexception { ...//Handle all @rabbitlistener annotated methods for (Listenermethod Lm:metadata.listenerMethods) {for (rabbitlistene
R rabbitListener:lm.annotations) {Processamqplistener (Rabbitlistener, Lm.method, Bean, beanname); }}//Handle all @rabbithandler annotated methods if (Metadata.handlerMethods.length > 0) {processmultimethodliste
Ners (metadata.classannotations, Metadata.handlermethods, Bean, beanname);
} return bean; } private void Processmultimethodlisteners (rabbitlistener[] classlevellisteners, method[] multimethods, Object Bean,
String beanname) {... for (Rabbitlistener classlevellistener:classlevellisteners) {//Create a class that handles multiple listening methods
Multimethodrabbitlistenerendpoint endpoint = new Multimethodrabbitlistenerendpoint (checkedmethods, Bean);
Endpoint.setbeanfactory (this.beanfactory); ProCesslistener (endpoint, Classlevellistener, Bean, Bean.getclass (), beanname);
}} protected void Processamqplistener (Rabbitlistener Rabbitlistener, method method, Object Bean, String beanname) {
Method Methodtouse = CheckProxy (method, Bean);
Create a class that handles a single listening method methodrabbitlistenerendpoint endpoint = new Methodrabbitlistenerendpoint ();
Endpoint.setmethod (Methodtouse);
Endpoint.setbeanfactory (this.beanfactory);
Processlistener (endpoint, Rabbitlistener, Bean, Methodtouse, beanname);
}
8.3. Method Processlistener ()
Both methods Processmultimethodlisteners () and Processmultimethodlisteners () will go into Processlistener (), where the following is done:
1. The first step is to set the queue, priority, and exclusive wait for the Methodrabbitlistenerendpoint to listen based on the configuration parameters of the @rabbitlistener on the listening method.
2. The second step gets the Rabbitadmin instance and sets it to Methodrabbitlistenerendpoint
3. The third step gets rabbitlistenercontainerfactory based on the value of the containerfactory () configuration of @rabbitlistener, the default value is NULL
4. The fourth step calls the tool class Rabbitlistenerendpointregistrar to register Rabbitlistenerendpoint to Rabbitlistenerendpointregistry. The Rabbitlistenerendpointregistrar class will be explained later.
protected void Processlistener (Methodrabbitlistenerendpoint endpoint, Rabbitlistener rabbitlistener, Object Bean, Obje CT admintarget, String beanname) {//There is a queue, priority, exclusive wait set Methodrabbitlistenerendpoint endpoint to listen to ...//get Rab
Bitadmin instance, and set to Methodrabbitlistenerendpoint in String rabbitadmin = Resolve (Rabbitlistener.admin ()); if (Stringutils.hastext (rabbitadmin)) {assert.state (this.beanfactory! = NULL, "Beanfactory must bes set to resolve
Rabbitadmin by bean name ");
try {endpoint.setadmin (This.beanFactory.getBean (Rabbitadmin, Rabbitadmin.class)); } catch (Nosuchbeandefinitionexception ex) {throw new Beaninitializationexception ("Could not register Rabbit Listener endpoint on ["+ Admintarget +"], no "+ RabbitAdmin.class.getSimpleName () +" with ID "
"+ Rabbitadmin +" ' is found in the application context ", ex); }}//According to @rabbitlistener's Containerfactory (The configured value gets rabbitlistenercontainerfactory rabbitlistenercontainerfactory<?> factory = null;
String containerfactorybeanname = Resolve (Rabbitlistener.containerfactory ()); if (Stringutils.hastext (Containerfactorybeanname)) {assert.state (this.beanfactory! = NULL, "Beanfactory must be s
ET to obtain container factory by bean name ");
try {factory = This.beanFactory.getBean (Containerfactorybeanname, Rabbitlistenercontainerfactory.class); } catch (Nosuchbeandefinitionexception ex) {throw new Beaninitializationexception ("Could not reg
Ister Rabbit Listener endpoint on ["+ Admintarget +"] for Bean "+ beanname +", no "+ RabbitListenerContainerFactory.class.getSimpleName () + "with Id '" + containerfactorybeanname + "' is fo
und in the application Context ", ex); }}//Call tool class Rabbitlistenerendpointregistrar to register Rabbitlistenerendpoint to RabbitlistenerendpointregistRy
Rabbitlistenerendpointregistra below will explain this class of This.registrar.registerEndpoint (endpoint, factory); }
9. Rabbitlistenerendpointregistrar
Register the rabbitlistenerendpoint above with the work class on Rabbitlistenerendpointregistry 9.1. Initialize Operation Afterpropertiesset ()
Initialization operation, this method is called by the Rabbitlistenerannotationbeanpostprocessor aftersingletonsinstantiated () method, triggering initialization. The main contents are as follows: First step: Cyclic Amqplistenerendpointdescriptor, Methodrabbitlistenerendpoint and The Rabbitlistenercontainerfactory instance calls Rabbitlistenerendpointregistry's method to register to this class on the second step: set startimmediately to True, indicating that the following will
Private Rabbitlistenerendpointregistry endpointregistry;?
...//Initialize operation @Override public void Afterpropertiesset () {registerallendpoints ();} protected void Registerallendpoints () {synchronized (this.endpointdescriptors) {, # AMQPLISTENERENDPOINTDESCR Iptor is to save Rabbitlistenerendpoint and Rabbitlistenercontainerfactory instances for (Amqplistenerendpointdescriptor descriptor:t his.endpointdescriptors) {# Methodrabbitlistenerendpoint endpoint + rabbitlistenercontainerfactory registered to Endpoin Tregistry on This.endpointRegistry.registerListenerContainer (Descriptor.endpoint, Resolvecontai
Nerfactory (descriptor)); } # Set the value to true this.startimmediately = true; Trigger immediate Startup} # get Rabbitlistenercontainerfactory instance/** if endpoint node registration is Rabbitlistenercontaine
Rfactory, this value is used (actually from the containerfactory () value of @rabbitlistener). If not, the default rabbitlistenercontainerfactory is used, and if not, the name of the Containerfactorybeanname value is obtained from the spring container.Rabbitlistenercontainerfactory object and set to default value Before we knew this value was rabbitlistenerannotationbeanpostprocessor in aftersingletonsinstantiated () Set to Rabbitlistenercontainerfactory **/private rabbitlistenercontainerfactory<?> resolvecontainerfactory ( Amqplistenerendpointdescriptor descriptor) {if (descriptor.containerfactory! = null) {return Descriptor.cont
Ainerfactory;
} else if (this.containerfactory! = null) {return this.containerfactory; } else if (this.containerfactorybeanname! = null) {assert.state (this.beanfactory! = NULL, "Beanfactory must b
E set to obtain container factory by bean name "); This.containerfactory = This.beanFactory.getBean (This.containerfactorybeanname, Rabbitlistenercontainerfactor
Y.class); return this.containerfactory; Consider changing this if live change of the factory is required} else {throw new Illegalstateexcepti On ("Could not resolve the" + RabbitlistenercoNtainerFactory.class.getSimpleName () + "to use for [" + Descriptor.endpoint + "] No factory is given and no
Default is set. ");}}
9.2. Registerendpoint () method
This method is called by the Rabbitlistenerannotationbeanpostprocessor Processlistener () method. The main contents are as follows:
In the previous analysis we initialized the set startimmediately=true and now only parse the true condition. This calls Rabbitlistenerendpointregistry's Registerlistenercontainer () method and passes the parameters startimmediately=true
In Rabbi