Detailed explanation of Spring's ImportSelector interface

Written in the forefront

Previous article - detailed introduction to Spring's ImportSelector interface (1) http://www.jianshu.com/p/aa99a303bc37 In the end, we left two questions. Let's talk about the first question today - how is ImportSelector called by the Spring framework?

How is the ImportSelector called by the Spring framework?

ClassPathXmlApplicationContext

Let's take ClassPathXmlApplicationContext as an example to see how Spring calls the ImportSelector interface during startup.

 
 
Constructor 1 of ClassPathXmlApplicationContext
 

As shown in the figure above, when initializing a ClassPathXmlApplicationContext, we need to specify an xml file as the application context. Another constructor is called in this constructor. Let's go in and see:

 
 
 
Constructor 2 of ClassPathXmlApplicationContext
 

From the above code, we can see that the refresh() method was invoked after the configLocations was set. So what does the refresh() method do?

refresh() method of AbstractApplicationContext

refresh() is defined in the ConfigurableApplicationContext interface. The specific implementation is in AbstractApplicationContext, as follows:

 
 
AbstractApplicationContext.refresh()

From the above implementation, we can see that this method has done a lot of preparation, initialization, event notification and so on. We won't look at the details one by one. Today we only care about those invokeBeanFactoryPostProcessors method.

 

 

 
 
AbstractApplicationContext.invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)

From the above code, we can see that the invokebenfactorypostprocessors() method delegates the specific calling method to the PostProcessorRegistrationDelegate class. In addition, from the name of the method, we can see that the main purpose of the method is to instantiate and call the BeanFactoryPostProcessor class. BeanFactoryPostProcessor is an interface with many implementation classes. Here, we don't care about the specific use of BeanFactoryPostProcessor. Let's get in first Post processor registration delegate.

 

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(xxx) method

public static void invokeBeanFactoryPostProcessors(
            ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

        // Invoke BeanDefinitionRegistryPostProcessors first, if any.
        // If you have BeanDefinitionRegistryPostProcessors, call them first
        // Processed Beans
        Set<String> processedBeans = new HashSet<String>();
        // Whether it is a beanfactory of BeanDefinitionRegistry type. BeanDefinitionRegistry can be used to register, delete and obtain BeanDefinitions
        if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        // BeanFactoryPostProcessor collection of normal type
            List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<BeanFactoryPostProcessor>();
        // BeanFactoryPostProcessor collection of type BeanDefinitionRegistry (BeanDefinitionRegistryPostProcessor inherits from BeanFactoryPostProcessor)
            List<BeanDefinitionRegistryPostProcessor> registryPostProcessors =
                    new LinkedList<BeanDefinitionRegistryPostProcessor>();
                        
        // Classify the collection beanFactoryPostProcessors. If it is BeanFactoryPostProcessor of BeanDefinitionRegistry type, call the method - postProcessBeanDefinitionRegistry(). ,
        //  postProcessBeanDefinitionRegistry(BeanDefinitionRegistry) the function of this method is to modify the beanDefinition within the container context after the standard BeanDefinitions are initialized.
            for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
                if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                    BeanDefinitionRegistryPostProcessor registryPostProcessor =
                            (BeanDefinitionRegistryPostProcessor) postProcessor;
                    registryPostProcessor.postProcessBeanDefinitionRegistry(registry);
                    registryPostProcessors.add(registryPostProcessor);
                }
                else {
                    regularPostProcessors.add(postProcessor);
                }
            }

            // Do not initialize FactoryBeans here: We need to leave all regular beans
            // uninitialized to let the bean factory post-processors apply to them!
            // Separate between BeanDefinitionRegistryPostProcessors that implement
            // PriorityOrdered, Ordered, and the rest.
            // Do not initialize factory beans here. We need to keep these ordinary beans not initialized here, so that bean factory post processor can handle them.
           // According to different interfaces implemented by BeanDefinitionRegistryPostProcessors, split them to call them.
            String[] postProcessorNames =
                    beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

            // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
            // First, call BeanDefinitionRegistryPostProcessors that implement the priority interface - PriorityOrdered 
            List<BeanDefinitionRegistryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>();
            for (String ppName : postProcessorNames) {
                if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                    priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
             // sort
            sortPostProcessors(beanFactory, priorityOrderedPostProcessors);
            // Add sorted priority PostProcessors to the registryPostProcessors collection
            registryPostProcessors.addAll(priorityOrderedPostProcessors);
            // Call the sorted priority BeanDefinitionRegistryPostProcessors, and the postProcessBeanDefinitionRegistry method will be called to modify, retrieve and delete the bean definition in the container context.
            invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);

            // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
            // Call BeanDefinitionRegistryPostProcessors that implements the Ordered interface. The general logic is the same as that of PriorityOrdered
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            List<BeanDefinitionRegistryPostProcessor> orderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>();
            for (String ppName : postProcessorNames) {
                if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                    orderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
            sortPostProcessors(beanFactory, orderedPostProcessors);
            registryPostProcessors.addAll(orderedPostProcessors);
            invokeBeanDefinitionRegistryPostProcessors(orderedPostProcessors, registry);

            // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
            // Call all other types of BeanDefinitionRegistryPostProcessors
            boolean reiterate = true;
            while (reiterate) {
                reiterate = false;
                postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
                for (String ppName : postProcessorNames) {
                    if (!processedBeans.contains(ppName)) {
                        BeanDefinitionRegistryPostProcessor pp = beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class);
                        registryPostProcessors.add(pp);
                        processedBeans.add(ppName);
                        pp.postProcessBeanDefinitionRegistry(registry);
                        reiterate = true;
                    }
                }
            }

            // Now, invoke the postProcessBeanFactory callback of all processors handled so far.
            // Call the postProcessBeanFactory method of all beandefinitionregistrypostprocessors.
            invokeBeanFactoryPostProcessors(registryPostProcessors, beanFactory);
           // Call a bean of type BeanFactoryPostProcessor in the container (excluding beans of type BeanDefinitionRegistryPostProcessor).
            invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
        }

        else {
            // Invoke factory processors registered with the context instance.
            // If beanFactory is not of type BeanDefinitionRegistry, the postProcessBeanFactory method of all beanfactorypostprocessors in the container is called directly.
            invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
        }

          // The following is the specific calling process of BeanFactoryPostProcessor, which is very similar to the above process, not to mention here.
        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let the bean factory post-processors apply to them!
        String[] postProcessorNames =
                beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

        // Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
        // Ordered, and the rest.
        List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
        List<String> orderedPostProcessorNames = new ArrayList<String>();
        List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
        for (String ppName : postProcessorNames) {
            if (processedBeans.contains(ppName)) {
                // skip - already processed in first phase above
            }
            else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
            }
            else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                orderedPostProcessorNames.add(ppName);
            }
            else {
                nonOrderedPostProcessorNames.add(ppName);
            }
        }

        // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
        sortPostProcessors(beanFactory, priorityOrderedPostProcessors);
        invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

        // Next, invoke the BeanFactoryPostProcessors that implement Ordered.
        List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
        for (String postProcessorName : orderedPostProcessorNames) {
            orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
        }
        sortPostProcessors(beanFactory, orderedPostProcessors);
        invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

        // Finally, invoke all other BeanFactoryPostProcessors.
        List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
        for (String postProcessorName : nonOrderedPostProcessorNames) {
            nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
        }
        invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

        // Clear cached merged bean definitions since the post-processors might have
        // modified the original metadata, e.g. replacing placeholders in values...
        beanFactory.clearMetadataCache();
    }

In the above call relationship, we can see. In this method, two types of postprocessors are called for processing, and the relationship between the two types of postprocessors is as follows:

 
 
 
BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor inherits from BeanFactoryPostProcessor. Let's take a look at the methods of interface definition

 
 
BeanFactoryPostProcessor

postProcessBeanFactory is used to modify beanFactory in the container context after standard initialization. All bean definitions will be loaded, but they will not be instantiated for the time being, which allows you to override or even add some properties to the delayed initialization bean.

 
 
BeanDefinitionRegistryPostProcessor
 

The postProcessBeanDefinitionRegistry method can modify the bean definition registry inside the container after the standardization initialization. All regular bean definitions will be loaded, but no bean will be instantiated. This allows us to add some new bean definitions in the next processing phase.

Combining the above logic, the postProcessBeanDefinitionRegistry is called before the postProcessBeanFactory method.

ConfigurationClassPostProcessor

So much has been said above to illustrate our protagonist, configuration class postprocessor. The ConfigurationClassPostProcessor class implements the BeanDefinitionRegistryPostProcessor interface, as well as some other interfaces, as shown in the following figure:

 
 
ConfigurationClassPostProcessor

The ConfigurationClassPostProcessor class implements the postProcessBeanDefinitionRegistry and postProcessBeanFactory methods of the parent class, as shown in the following figure:

 
 
 
ConfigurationClassPostProcessor implements the parent method

From the above analysis, we can see that the call of the postProcessBeanDefinitionRegistry method is earlier than the postProcessBeanFactory method. Let's first look at the details of the postProcessBeanDefinitionRegistry method:

 
 
postProcessBeanDefinitionRegistry
 

From the annotation, we can see that the main function of this method is to further obtain the bean definition from the Configuration, that is, the Configuration class decorated by the @ Configuration annotation. In this method, processConfigBeanDefinitions() method is called again, which is mainly used to process @ Configuration annotation.

In the processConfigBeanDefinitions method, the parse method of the ConfigurationClassParser class is called to parse @ Configuration, as shown in the following figure:

 
 
 
image.png

Let's look at the parse method of the ConfigurationClassParser class, as follows:

 
 
 
ConfigurationClassParser.parse()

See here, we finally see the dawn, and finally see the DeferredImportSelector interface. We ignore the rest of the code in the method and go directly to the processDeferredImportSelectors() method to have a look.

 
 
 
processDeferredImportSelectors

In the processDeferredImportSelectors method, the processImports method is called to execute the import, followed by a look. There is a very important code in the processImports method, which is as follows:

 
 
processImports
 

As shown in the code above:

  1. Check if the type is ImportSelector, and if so, perform step 2.
  2. If both the Aware interface and the ImportSelector interface are implemented, the implementation method of the Aware interface is called first, and then the third step is entered.
  3. Call the selectimporters method of selector, take the metadata information of the class to be imported as a parameter, and return the classes to be imported according to the specific implementation logic.
  4. Execute subsequent code

At this point, we can see that the implementation method of the ImportSelector interface, selectImports, is finally called successfully.

summary

  1. The import implementation of the ImportSelector is implemented through the BeanDefinitionRegistryPostProcessor, a sub interface of the BeanFactoryPostProcessor interface.
  2. The processing of the ImportSelector interface is accompanied by the processing of the @ Configuration annotation.
  3. The ImportSelector interface enables custom conditional selective import of classes.
  4. The word interface of the ImportSelector interface, DeferredImportSelector, is not processed until all @ Configuration processing is completed.
  5. When processing the ImportSelector interface, the bean definition has been loaded, but the bean has not yet been instantiated.
  6. Spring Bootn's auto configuration function is achieved through the implementation class EnableAutoConfigurationImportSelector of the DeferredImportSelector interface.

 

In the process of automatic configuration parsing, classes are parsed to beanfactory in two ways;

First, the components scanned by components, excluding the automatically annotated classes, are directly placed in the bean factory after being scanned;

Second, for other classes such as using Import annotation such as EnableAutoConfiguration, she first loads the auto configured classes into a map, and then exits to the external post processor ConfigurationClassPostProcessor to load and parse them into beanfactory after the class resources are loaded;

Map<ConfigurationClass, ConfigurationClass> configurationClasses =
      new LinkedHashMap<ConfigurationClass, ConfigurationClass>();
this.reader.loadBeanDefinitions(configClasses);

In addition, the configuration loading initialization of spring's non lazy loading is implemented in the last step of refreshcontext();

refresh()#AbstractApplicationContext.finishBeanFactoryInitialization(beanFactory);


Author: Da Dao to Jane Andy
Link: https://www.jianshu.com/p/23d4e853b15b
Source: Jianshu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.

Tags: Programming Spring REST xml

Posted on Sun, 19 Apr 2020 04:01:30 -0700 by moboter