SpringBoot growth 7: how does the extension operation of the container execute

Keywords: Java Spring Boot

At present, the code we analyze has reached the SpringBoot principle related to container processing. The code is as follows:

public ConfigurableApplicationContext run(String... args) {
   //DONE extension point SpringApplicationRunListeners listeners.starting();

   //Handling and abstract encapsulation of DONE configuration file ConfigurableEnvironment

   //Container related treatment
   //1) The core is to create Context and BeanFactory objects, internally initialize Reader and Scanner, and load some internal beans
   context = createApplicationContext();
   exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                          new Class[] {ConfigurableApplicationContext.class }, context);
    //2) Set a bunch of properties and components for the container Context and BeanFactory, and execute the extension point of initialize/listener
    //The more important attributes are singletonObjects, beanDefinitionMap, beanfactoryprocessors, and applicationListeners
    prepareContext(context, environment, listeners, applicationArguments,printedBanner);
    //3) The key extension operations of todo container are implemented, which is also the extension of many container functions and third-party functions
    refreshContext(context);
   //Other logic
}

The analyzed stages are shown in the figure below:

After prepareContext() is ready, the next step is refreshContext(). The key extension operations of the container have been executed, which is also the extension of many container functions and third-party functions. Let's take a look together.

Quickly touch the context of refreshCotenxt

The refreshCotenxt() method finally calls the container's refresh method. Let's take a look at its context first, and then find the key point from the middle.

Let's take a quick look at its code context:

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

The whole is composed of a try catch. There are many methods inside. It seems that people can't find the key point. I feel that each method is very important.

When I first saw it, each method was analyzed separately, from context to detail.

Finally, focus on the big and let go of the small. In fact, the three most important methods of refresh are:

Invokebeanfactoryprocessors implements container extension points, automatic assembly configuration, and common extensions of other technologies

The onRefresh embedded web container starts, and the default is tomcat

Instantiation of finishBeanFactoryInitialization bean

Then, based on the idea of focusing on the big and letting go of the small, the other methods are not very important. This confirmation process will not take you to see each method one by one.

Of course, in addition to the core analysis of the above three methods, others will be mentioned in passing to let you know.

Today, let's refresh to see what the first core method does.

Invokebeanfactoryprocessors perform the main operations before the container extension point

The implementation of refresh() to invokeBeanFactoryPostProcessors is a very important logic. The previous methods can be summarized as shown in the following figure:

In the whole process, the contents marked in light blue are not very important:

It involves setting some unimportant values, such as startupDate, setSerializationId, BeanExpressionResolver, and so on

It also involves the initialization of the basic object collection, earlyApplicationEvents and earlyApplicationListeners

Several interfaces that need to be specially considered and ignored by container injection objects are also marked

setignoreDependencyInterface sets the ignored interface and will not be registered as a bean

registerResolvableDependency indicates the container objects that some Spring internal interfaces will inject by default

The relatively important point is that the following is marked in green:

It mainly adds some of Spring's own Bean extension points, BeanPostProcessor, Spring's default BeanPostProcessor, BeanDefinition and registerSingleton, and some internal objects to the collection.

What is the term BeanPostProcessor?

before BeanFactoryPostProcessor It is an extension of the container. It mainly has a method to set properties for the container, supplement some singleton objects, and supplement some BeanDefinition. 

that BeanPostProcessor Yes bean Extension of, with before and after Two kinds of methods, right Bean How to expand in bean Before and after the creation of bean Add some attributes, etc.

We'll just go through the logic before invokebeanfactoryprocessors. There is no particularly important logic, mainly Spring's internal processing, which adds a bunch of attributes to the container.

The core context of invokebeanfactoryprocessors

After having a general understanding of the main operations of invokebeanfactoryprocessors, let's first take a look at the context of this method to see what it mainly does?

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
   PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

   // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
   // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
   if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
   }
}

At first glance, this method seems quite simple. There are only two paragraphs of logic. It's easy for you to get to the point

Invokebeanfactoryprocessors executes the extension point, which should be the extension point of the core trigger container.

According to the conditions, add a Bean extension operation, BeanPostProcessor, which is obviously not the key logic. Many similar operations have been done before.

As shown in the figure below:

If you go deep into the method of PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors, you will find the following big pile of code:

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

        // Invoke BeanDefinitionRegistryPostProcessors first, if any.
        Set<String> processedBeans = new HashSet<>();

        if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
            List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
            List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

            for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
                if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                    BeanDefinitionRegistryPostProcessor registryProcessor =
                            (BeanDefinitionRegistryPostProcessor) postProcessor;
                    registryProcessor.postProcessBeanDefinitionRegistry(registry);
                    registryProcessors.add(registryProcessor);
                }
                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.
            List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

            // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
            String[] postProcessorNames =
                    beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();

            // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();

            // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
            boolean reiterate = true;
            while (reiterate) {
                reiterate = false;
                postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
                for (String ppName : postProcessorNames) {
                    if (!processedBeans.contains(ppName)) {
                        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                        processedBeans.add(ppName);
                        reiterate = true;
                    }
                }
                sortPostProcessors(currentRegistryProcessors, beanFactory);
                registryProcessors.addAll(currentRegistryProcessors);
                invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
                currentRegistryProcessors.clear();
            }

            // Now, invoke the postProcessBeanFactory callback of all processors handled so far.
            invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
            invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
        }

        else {
            // Invoke factory processors registered with the context instance.
            invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
        }

        // 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<>();
        List<String> orderedPostProcessorNames = new ArrayList<>();
        List<String> nonOrderedPostProcessorNames = new ArrayList<>();
        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(priorityOrderedPostProcessors, beanFactory);
        invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

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

        // Finally, invoke all other BeanFactoryPostProcessors.
        List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
        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();
    }

This method seems a little complicated at first, but it doesn't matter. You can find out its context first:

1) First, it mainly consists of an if else

2) This is followed by three consecutive for loops

As shown below:

Well, this is the core context of this method. Next, let's find out what the if else logic is doing and what the next three for loops are doing. This method basically knows what it is doing.

Let's look at what the first if else is doing?

If esle core context logic

The first if esle core logic mainly determines whether the container implements the BeanDefinitionRegistry interface, so as to determine how to execute the extension operation of beanfactoryprocessor.

BeanDefinitionRegistry, which we popularized before, encapsulates the interface for common operations of BeanDefinition. The container implements this interface by default, so it also represents the container. The List in the container can be maintained through the implementation method .

The code is as follows:

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

    }else {
       // Invoke factory processors registered with the context instance.
       invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
    }
}

By default, the container implements the BeanDefinitionRegistry interface and normally executes if logic. Because if logic is relatively complex, let's first look at what else logic is doing, and then understand if logic.

else logic

The else logic is relatively simple. It mainly triggers the extension method postProcessBeanFactory() of beanfactoryprocessors in the input parameter. The code is as follows:

private static void invokeBeanFactoryPostProcessors(
    Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {

    for (BeanFactoryPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessBeanFactory(beanFactory);
    }
}

Question: where does the internal beanfactoryprocessor in the input parameter come from?

Is through a property List from the container beanFactoryPostProcessors.

This attribute refers to some Spring internal beanfactoryprocessors added through extension points such as listener. There are three main:

beanFactoryPostProcessors = {ArrayList@2882}  size = 3
 0 = {SharedMetadataReaderFactoryContextInitializer
 $CachingMetadataReaderFactoryPostProcessor@2887} 
 1 = {ConfigurationWarningsApplicationContextInitializer
 $ConfigurationWarningsPostProcessor@2888} 
 2 = {ConfigFileApplicationListener
 $PropertySourceOrderingPostProcessor@2889} 

We call them inernalbean factorypostprocessors here

As shown below:

In fact, the final else logic mainly triggers the postProcessBeanFactory() extension method of these internal beanfactory postprocessors. The whole is shown in the figure below:

As for what these extension operations do, we will analyze later. First, we will find out the overall context of the method and look at the details.

if logic

After understanding the logic of else, let's take a look at what if does. Because the if else logic will not be executed by default, the priority must be if.

Here we need to popularize some concepts before we can better understand the code logic of if.

What is the term bean definition registry postprocessor?

BeanDefinitionRegistryPostProcessor
 It is also an extension point, inherited from BeanFactoryPostProcessor,yes BeanFactoryPostProcessor Just added an extension method.

The overall design is shown in the figure below:

Beanfactoryprocessor can have two extension operations

In other words, the original beanfactory postprocessor extension method has been increased from one to two, one is postProcessBeanFactory(), and the other is postProcessBeanDefinitionRegistry().

Another thing to emphasize is that there are two sources of beanfactoryprocessor

1) In the container, the beanfactoryprocessor added through the extension point in advance

2) Beanfactoryprocessor defined in BeanDefinition but not instantiated

As shown below:

Beanfactory postprocessor can have two extension operations and two sources of beanfactory postprocessor

These two points are very important. With this knowledge, it will be easy for us to look at if logic.

The main codes of if logic are as follows:

if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
            List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
            List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

            for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
                if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                    BeanDefinitionRegistryPostProcessor registryProcessor =
                            (BeanDefinitionRegistryPostProcessor) postProcessor;
                    registryProcessor.postProcessBeanDefinitionRegistry(registry);
                    registryProcessors.add(registryProcessor);
                }
                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.
            List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

            // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
            String[] postProcessorNames =
                    beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();

            // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();

            // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
            boolean reiterate = true;
            while (reiterate) {
                reiterate = false;
                postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
                for (String ppName : postProcessorNames) {
                    if (!processedBeans.contains(ppName)) {
                        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                        processedBeans.add(ppName);
                        reiterate = true;
                    }
                }
                sortPostProcessors(currentRegistryProcessors, beanFactory);
                registryProcessors.addAll(currentRegistryProcessors);
                invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
                currentRegistryProcessors.clear();
            }

            // Now, invoke the postProcessBeanFactory callback of all processors handled so far.
            invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
            invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}

The main logic of this if logic is that there are three for+1while logic, which can be divided according to the execution of extension operation 1 and extension operation 2.

Let's look at it separately.

Execute extension method 1: postProcessBeanDefinitionRegistry()

When executing extension method 1, you need to start from two sources respectively, and execute the beanfactorypost processor that implements BeanDefinitionRegistryPostProcessor.

The main logic can be summarized as follows:

The words in the figure above are explained in words:

1) In the container, did the previously added internal related beanfactoryprocessor implement this BeanDefinitionRegistryPostProcessor interface and add the extension method postProcessBeanDefinitionRegistry()? If yes, all corresponding beanfactoryprocessors execute this method through the for loop. And record the executed beanfactoryprocessor and the unexecuted beanfactoryprocessor.

2) In the container, in the previously added internal related BeanDefinition, is there defined as beanfactoryprocessor? If so, execute the extension method postProcessBeanDefinitionRegistry() according to the implementation of prioryorder interface, Order interface and no Order interface, using 2for loop + a while loop, These beanfactoryprocessors are recorded after execution.

Execute extension method 2: postProcessBeanFactory()

All beanfactoryprocessors recorded when extension method 1 was executed previously, including those added before the extension point and defined by BeanDefinition.

We can use these recorded beanfactoryprocessors to execute the extension method 2 - postProcessBeanFactory().

As shown in the figure below:

We will find out the logical context of the entire if else. As for what these extension operations do, we will analyze later, or first find out the method context as a whole to see the details.

Core context logic of three For loops

In the core context of invokebeanfactoryprocessors, in addition to an if else logic, the next is the execution of three consecutive for loops.

It is divided into three categories: beanfactory postprocessor, which mainly implements the postProcessBeanFactory method of the extension point beanfactory postprocessor.

This logic sounds like the logic in the previous if else. Only the BeanDefinitionRegistryPostProcessor was executed before.

Moreover, the beanfactoryprocessor at this time comes from the in BeanDefinition.

You may say that the beanfactoryprocessor in BeanDefinition has been executed before. How else?

Previously, some beanfactoryprocessors defined in Spring were executed. After executing the if else logic, more third-party and other beanfactoryprocessors under ClassPath were actually scanned

These newly scanned beanfactoryprocessors refer to the previous execution method of BeanDefinitionRegistryPostProcessor and perform the following extension operations:

The logical context of the three for is not complicated. As for what these extension operations do, now that we know the context of the whole method invokebeanfactoryprocessors, we will analyze it in the next section.

Summary

Finally, in a brief summary, invokebeanfactoryprocessors mainly implement the two extension methods of BeanDefinitionRegistryPostProcessor and beanfactoryprocessor. These beanfactoryprocessors may be added by the internal Spring implementation, or they may be beanfactoryprocessors scanned from ClassPath.

What do these extension points write and what are the key operations? Let's take a closer look at the details in the next section. See you next time!

This article is composed of blog one article multi posting platform OpenWrite release!

Posted by Dizzee15 on Mon, 27 Sep 2021 16:11:49 -0700