Comprehensive analysis of SpringBoot configuration loading principle

One, loading logic

1.1) Load Genesis post processor

Let's take an example:

final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext([your configuration class].class);

Pay attention to the AnnotationConfigApplicationContext object here, follow up the source code:

//In the parameterless construction method, you will see the following code  
this.reader = new AnnotatedBeanDefinitionReader(this);

AnnotatedBeanDefinitionReader:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { 
   Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); 
   Assert.notNull(environment, "Environment must not be null"); 
   //Assign the ApplicationContext object to AnnotatedBeanDefinitionReader 
   this. registry = registry; 
   //User processing condition expression calculation @Conditionl host, er, 
   this.conditionEvaluator = new ConditionEvaluator(registry, environment, null); 
   //Here is an earth-shattering thing, registering a bunch of configured processors, These processors are called "Genesis processors" because these processors play a decisive role in the subsequent processing 
   AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); 
}

I said before that a bunch of configured processors-Genesis objects are registered in AnnotationConfigUtils.registerAnnotationConfigProcessors. Note: All registered here are Bean definitions, all registered here are Bean definitions, and all registered here are Bean definitions. Important things are said three times; among them, a "ConfigurationClassPostProcessor" is registered. This Genesis post processor is specifically used for processing. Our configuration class (there are other genesis processors for parsing annotations, this is not our focus today, let them go first), he is a post processor of a Bean factory;

clipboard.png

From the class diagram, you can see that this class implements the BeanFactoryPostProcessor and BeanDefinitionRegistryPostProcessor interfaces. Why is it a BeanDefinitionRegistryPostProcessor instead of a BeanFactoryPostProcessor? The previous content mentioned that BeanFactoryPostProcessor can process and modify Bean definitions. BeanDefinitionRegistryPostProcessor is an enhancement of BeanFactoryPostProcessor, which can not only process and modify but also register;

1.2) Registration of configuration classes

Register the incoming configuration class through the register method through the second sentence of code (register(annotatedClasses) method) in the AnnotationConfigApplicationContext construction method. Here, the Bean definition is still registered;

//The reader here is the AnnotatedBeanDefinitionReader object mentioned in 1.1  
this.reader.register(annotatedClasses);

At this point, the processor and configuration class for parsing the configuration are available, so the next step is how to deal with it; pay special attention: here finally the configuration class is registered through only the org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean method Bean definitions have nothing to do with Spring caching, processing, and parsing. This is the Spring ecosystem's routines first becoming Bean definitions, and then parsing and processing; these Bean definitions are stored in the container BeanDefinitionMap;

1.3) Start processing

Here is the Spring core refresh method

/* 
* IOC container refresh interface 
* This method is the core method; 
* It is the embodiment of the entire Spring ecology; 
* It reflects the entire life cycle of the IOC, including the life cycle of Bean initialization, Bean loading, use, and destruction 
* This method contains The 13 core methods are the essence of Spring's Jiuyin Sutra. After understanding these 13 methods, you really understand Spring 
* */ 
refresh();

So in which method is the parsing of the configuration class specifically carried out, don't worry, take your time;

/* 
* 5: Call the post processor of the bean factory 
* Here, the Bean that meets the conditions is turned into a Bean Definition (BeanDefinition) 
* Here is the real analysis of the class and the element annotations in the class 
* For example: @Configuration, @ ComponentScan, @Bean, etc. finally put these Bean definitions in the BeanDefinitionMap 
* There must be a cause and effect, here is the creation of the Genesis Processor Bean Definition (BeanDefinition) created before: ConfigurationClassPostProcessor; that is, a separate call to getBean 
* Because of the Genesis of ConfigurationClassPostProcessor The processor object has absolute priority. Genesis processors have not been created, so there is a fart behind 
* */ 
invokeBeanFactoryPostProcessors(beanFactory);

It is here that the post processor of the Bean factory is called for processing. Contact the above content and think about whether the post processor called here has and only the Genesis Bean definition and the configuration class I passed in Bean definition, and ConfigurationClassPostProcessor is an implementation of the post processor interface of a Bean factory, so the configuration class is processed here;

PS: Here is a question that is often asked in interviews. When calling the ConfigurationClassPostProcessor Genesis post processor, it must first call the postProcessBeanDefinitionRegistry method of the BeanDefinitionRegistryPostProcessor interface with registration function to process the Bean definition of the configuration class, and then call the BeanFactoryPostProcessor interface The postProcessBeanFactory method is registered;

2. Processing logic

/* 
* Pass in the bean factory and get the bean factory post processor in the applicationContext (but because there is no instantiation process, the passed in is empty) 
* getBeanFactoryPostProcessors(): The current Bean factory post processor set, here and It is not registered in the built-in 
* This collection is empty when it is first called; 
* This container is used to manually call the ApplicationContext.addBeanFactoryPostProcessor() method after initializing the AnnotationConfigApplicationContext externally; 
* It is the Bean manually added by yourself Factory post processor; 
* */ 
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

In PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors, it is roughly divided into two steps; the first step: call all the Bean Definition PostProcessor that implements the registration function (BeanDefinitionRegistryPostProcessor); the second step: call all the postprocessors that implement the Bean Factory );

Today, I will focus on the content of the first step. Enter the invokeBeanFactoryPostProcessors method in the PostProcessorRegistrationDelegate class; high energy ahead, prepare the tissues, and start rolling:

2.1) The main part of the source code

The first step can be to complete the analysis and loading of the configuration by the Genesis processor ConfigurationClassPostProcessor; in all Genesis processors, only ConfigurationClassPostProcessor implements the BeanDefinitionRegistryPostProcessor interface;

/* 
* The first step: first call the post processor of BeanDefinitionRegistryPostProcessor 
* First time comes in, only the Genesis processor Bean definition and the configuration class Bean definition passed in by the constructor 
* Genesis processor: ConfigurationClassPostProcessor is the post processor of the Bean definition with registration function Processor (BeanDefinitionRegistryPostProcessor) implementation; 
* So here should be ConfigurationClassPostProcessor meets the conditions; 
* */ 
//Judging that our beanFacotry implements BeanDefinitionRegistry 
if (beanFactory instanceof BeanDefinitionRegistry) { 
   //Forcibly convert our bean factory to BeanDefinitionRegistry 
   BeanDefinitionRegistry registry = ( BeanDefinitionRegistry) beanFactory; 
   //Save the post of BeanFactoryPostProcessor type 
   List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();  
   the post-processor of the //Save the post-processor of the BeanDefinitionRegistryPostProcessor type
   List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>(); 

   /*
   * BeanFactoryPostProcessors passed in in a loop 
   * If this collection does not manually call the addBeanFactoryPostProcessor method after the AnnotationConfigApplicationContext is initialized earlier 
   * then this collection is empty 
   * */ 
   for (BeanFactoryPostProcessor postProcessor: beanFactoryPostProcessors) { 
      //Judging whether our post processor is a BeanDefinitionRegistryPostProcessor 
      if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { 
         //Forced conversion 
         BeanDefinitionRegistryPostProcessor registryProcessor = 
               (BeanDefinitionRegistryPostProcessor) postProcessor;  
         //Call him as the post method of the processor of BeanDefinitionRegistryPostProcessor
         registryProcessor.postProcessBeanDefinitionRegistry(registry); 
         //Add to the collection of BeanDefinitionRegistryPostProcessor we used to save
         registryProcessors.add(registryProcessor); 
      } 
      else {//If the BeanDefinitionRegistryPostProcessor interface is not implemented, then he is BeanFactoryPostProcessor 
         //Add the current post processor to regularPostProcessors 
         regularPostProcessors.add(postProcessor); 
      } 
   } 

   //Define a collective user Save the currently prepared BeanDefinitionRegistryPostProcessor 
   List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>(); 

   /* 
   * Step 1.1: Go to the container to obtain the processor name of the bean of the BeanDefinitionRegistryPostProcessor 
   * This is actually calling the Genesis processor ConfigurationClassPostProcessor 
   * */  
   String[] postProcessorNames =
         beanFactory .getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
   //Loop the type name of the BeanDefinitionRegistryPostProcessor obtained in the previous step 
   for (String ppName: postProcessorNames) { 
      /* 
      * Determine whether the PriorityOrdered interface is implemented 
      * If the PriorityOrdered interface is implemented, it will be called first 
      * This reflects that children's shoes that comply with the specification will have Priority; Genesis processor ConfigurationClassPostProcessor is a 
      child 's shoe that abides by the specification, so here will be called first 
      * */ if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { 
         //Get the object by calling getBean() displayed Then add it to the currentRegistryProcessors collection to 
         currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));  
         //At the same time, it is also added to the processedBeans collection
         processedBeans.add(ppName); 
      } 
   } 
   // Sort the BeanDefinitionRegistryPostProcessor in the currentRegistryProcessors collection through the value returned by the getOrder method in PriorityOrdered 
   sortPostProcessors(currentRegistryProcessors, beanFactory); 
   //Add it to the registry for saving to the 
   registryProcessors.addAll(currentRegistryProcessors ); 
   /** 
    * The typical BeanDefinitionRegistryPostProcessor here is ConfigurationClassPostProcessor 
    * It is used to load bean definitions, such as our package scanning, @import and so on. . . . . . . . . 
    * Here is the postProcessBeanDefinitionRegistry method in the Bean Definition 
   PostProcessor with registration function (BeanDefinitionRegistryPostProcessor) 
    */ invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); 
   //After the call is completed, immediately clea drops 
   currentRegistryProcessors.clear();

   //Go to the container to obtain the processor name of the bean of BeanDefinitionRegistryPostProcessor 
   postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); 
   //Loop the type name of the BeanDefinitionRegistryPostProcessor obtained in the previous step 
   for (String ppName: postProcessorNames) { 
      /* 
      * Have not been processed, and implement the Ordered interface, the above is to call the Bean definition post processor that implements the PriorityOrdered interface, here is the processing of the Bean definition post processor that implements the Ordered interface 
      * So you can draw a conclusion : PriorityOrdered interface has a higher priority than Ordered; 
      * */ 
      if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {  
         //The displayed method of calling getBean() gets the object and then Add to the currentRegistryProcessors collection
         currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)) ; 
         //Also added to the processedBeans collection
         processedBeans.add(ppName); 
      } 
   } 
   // Sort BeanDefinitionRegistryPostProcessors in the currentRegistryProcessors collection 
   sortPostProcessors(currentRegistryProcessors, beanFactory); 
   //Add him to registryProcessors for saving to 
   registryProcessors.addAll(currentRegistryProcessors); 
   /* 
   * Call his Post-processing method 
   * Here is the postProcessBeanDefinitionRegistry method in the Bean Definition 
   PostProcessor with registration function (BeanDefinitionRegistryPostProcessor) 
   * */ invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); 
   //After the call , clea immediately drops 
   currentRegistryProcessors.clear() ; 
   //Define a switch variable for repeated processing The default value is true

   //Call BeanDefinitionRegistryPostProcessor that does not implement any priority interface 
   boolean reiterate = true; 
   //You can come in for the first time 
   while (reiterate) { 
      //Enter the loop and immediately change the switch variable to fasle 
      reiterate = false; 
      //Go to the container Get the processor name of the bean of BeanDefinitionRegistryPostProcessor 
      postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); 
      //Loop the type name of BeanDefinitionRegistryPostProcessor obtained in the previous step 
      for (String ppName: postProcessorNames) { 
         // Unprocessed , That is, the Bean definition post processor that does not implement any priority interface (PriorityOrdered, Ordered) 
         if (!processedBeans.contains(ppName)) { 
            //The displayed method of calling getBean() gets the object and then adds it to the currentRegistryProcessors collection go with
            currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); 
       * */ 
            //Also added to the processedBeans collection
            processedBeans.add(ppName); 
            //Set to true again 
            reiterate = true; 
         } 
      } 
      //Sort the BeanDefinitionRegistryPostProcessor in the currentRegistryProcessors collection 
      sortPostProcessors(currentRegistryProcessors, beanFactory); 
      //Add it to the registryProcessors for saving to the 
      registryProcessors.addAll (currentRegistryProcessors); 
      /* 
       * Call his post-processing method 
       * Here is the postProcessBeanDefinitionRegistry method in the Bean Definition 
      PostProcessor with registration function (BeanDefinitionRegistryPostProcessor) invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); 
      //Clear 
      currentRegistryProcessors.clear (); 
   }
 
   /* 
   * Call the interface that implements BeanDefinitionRegistryPostProcessor; this interface implements BeanFactoryPostProcessor 
   * The actual call here is the postProcessBeanFactory method of BeanFactoryPostProcessor 
   * */ 
   invokeBeanFactoryPostProcessors(registryProcessors, beanFactory ); 
   //Call BeanFactoryPostProcessor 
   invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); 
} else {//If the current beanFacotory does not implement BeanDefinitionRegistry directly electrophoresis 
    //Direct electrophoresis method of beanFacotoryPostProcessor interface for post-processing 
   invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); 
} 
/* 
* End of the first step 
* ------------- ------------------------------------------------- 
* */

In this step, pay special attention to the invokeBeanFactoryPostProcessors method in the last few lines; enter this method:

private static void invokeBeanFactoryPostProcessors( 
      Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) { 
   /* 
   * This is the post processor that calls the Bean Factory. Note that this is not a registered function 
   * */ 
   for (BeanFactoryPostProcessor postProcessor: postProcessors) { 
      / * 
      * The following is another call from the wind, which is actually a hidden mystery; 
      * There must be a Genesis processor loaded initially-ConfigurationClassPostProcessor: This method really does a earth-shattering thing; 
      * * / 
      postProcessor.postProcessBeanFactory (beanFactory); 
   } 
}
/**
 * Prepare the Configuration classes for servicing bean requests at runtime
 * by replacing them with CGLIB-enhanced subclasses.
 */
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   int factoryId = System.identityHashCode(beanFactory);
   if (this.factoriesPostProcessed.contains(factoryId)) {
      throw new IllegalStateException(
            "postProcessBeanFactory already called on this post-processor against " + beanFactory);
   }
   this.factoriesPostProcessed.add(factoryId);
   if (!this.registriesPostProcessed.contains(factoryId)) {
      // BeanDefinitionRegistryPostProcessor hook apparently not supported...
      // Simply call processConfigurationClasses lazily at this point then. 
      processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory); 
   } 
   /* 
   * Special attention here is to use Cglib to proxy the configuration class in the following call; recall our configuration class Will there be methods 
   such as @Configuration, @Bean , etc. that use annotations; * Here is another common interview question; if we call the method annotated with @Bean annotations in the configuration class, then there will be 10 more methods At this time, the processing in this method will actually only be 
   called * once, because it is proxied, all calls except the first call are obtained from the buffer pool; this is also satisfied for Bean-oriented The idea of ​​programming, now that there is already, why do you have to repeat the process; 
   * Pay special attention to the type of Full when generating Bean Definition (BeanDefinition) only when the class annotated with @Configuration annotation is generated; in the in-depth source code, you can see that only Bean definition (Beandefinition) 
   * Will be proxied when the type is Full 
   * */ 
   enhanceConfigurationClasses(beanFactory); 
   beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); 
}

2.2) Configuration analysis execution process

The first step is actually to continuously call the post processor of the Bean factory. The calling process is simply the following sequence;

db41e76ac0a63711a031b2755ab4e098.png

Recalling the first chapter, we said that a Genesis processor ConfigurationClassPostProcessor will be created at startup. This processor implements both BeanDefinitionRegistryPostProcessor and BeanFactoryPostProcessor, so in this process it will be called in the first and fourth steps; The first step is to call the method in the post-processor with registration function, so in this step, the @Bean and other elements in the class will be processed; the elements are turned into Bean definitions; the fourth step is called without registration function Methods in the post processor;

Looking at a sample code, the following is an implementation of our custom Bean factory post-processor with registration function:

@Component
public class TestConfig implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        
    }
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

Assuming that I have implemented a BeanDefinitionRegistryPostProcessor in my code at present, it will be called in the third and fourth steps. This fully illustrates that Spring is powerful because of its ecology and can be called back in specific places for development. Full space for personnel to play;

(PS: Here is a more common interview question. When was the BeanDefinitionRegistryPostProcessor defined by ourselves loaded? Combined with the above, in the first step, there is only one Genesis processor ConfigurationClassPostProcessor. This The processor is specifically used to parse the configuration class. All our custom configurations are loaded through this Genesis post processor, so our custom post processor will only exist after the first step; PS : For children's shoes who want to abuse themselves, you can follow the code of the first step in depth);

clipboard.png

This step has only one Genesis post processor ConfigurationClassPostProcessor, so take a look at the implementation method of this post processor:

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { 
   int registryId = System.identityHashCode(registry); 
   if (this.registriesPostProcessed.contains(registryId)) { 
      throw new IllegalStateException( 
            "postProcessBeanDefinitionRegistry already called on this post-processor against "+ registry); 
   } 
   if (this.factoriesPostProcessed.contains(registryId)) { 
      throw new IllegalStateException( 
            "postProcessBeanFactory already called on this post-processor against "+ registry); 
   } 
   this.registriesPostProcessed.add(registryId); 
   //Real analysis of our bean definition , This method is too deep. When using this method, the bladder and large intestine had to be blown up before I found it; children who are interested in challenging can try it
   processConfigBeanDefinitions(registry);
}

The processConfigBeanDefinitions method is the processing of parsing and injection, and the call chain is very deep. Here is a mention: you should pay special attention to the methods starting with do in Spring. According to the Spring specification, these methods starting with do are actually methods of doing things;


Guess you like

Origin blog.51cto.com/15138908/2668826