Spring container initialization process

Public account: Perfect engineering

Gitte address: gitee.com/duchenxi/to…

This article introduces the process of starting the annotation-based Spring container. It mainly introduces the key code in the process. As for the details of the process, I will only briefly mention them in this article.

To summarize the overall process (mind mapping is also in my Gitte) :

First we create an annotation-based ApplicationContext, as shown in the following code:

You can see we created a AnnotationConfigApplicationContext such a annotation-based container, and introduced into a package path as the parameter, then instantiates a container into a sort of process?

We point AnnotationConfigApplicationContext constructor, from the above comments can see this structure method is used to construct a annotation-based container, and according to the package path from a given scanning components, Register the component definitions (the important BeanDefinition), and then refresh the container atomically. The general logic is clear from the three lines of code in the method: 1. Construct a container. 2. Scan the packet path. 3. Refresh.

Let’s take a closer look at these three processes.

this

If we click on this() and ignore the step records in the new version of Spring, we can see that the main logic is just two lines: construct a reader and scanner.

We point AnnotatedBeanDefinitionReader constructor, can see the method above the annotation of the method is used according to the given registry create a Reader object, and will create a standard environment. You can see that the method entry is a BeanDefinitionRegistry registry, And through the analysis of the above can know the actual data of the incoming is in fact AnnotationConfigApplicationContext AnnotationConfigApplicationContext container itself is indirectly inherited BeanDefinitionRe Gistry shows that the container contains the bean registry functionality.

As we continue with this method, we call another reader constructor, passing in the container and an environment. GetOrCreateEnvironment (Registry) creates an environment based on the registry, which can be used to get some system configuration. We won’t go into that here.

In the reader constructor, you can see a line of code that literally registers the annotation post-handler. Yes, this method will put some of the important annotation post-handler into the container.

When we click on this method, we can see from the annotations on the method that this method is using a post-processor that adds some annotations to the given registry. @Order, @lazy, @Configuration, @bean, @Autowire, @Value, @postConstruct, @EventListener, etc. I’ll write additional articles later on how the post-processor handles these annotations in detail.

About reader’s creation process here is about, followed by the creation process, scaner scaner creation process and the process is the same as the reader but in this process have a registered annotation filter, the process of registering the specified annotation filters, container when scanned for package to parse the specified annotation, Here we add an @Component annotation, and implicitly register the associated annotation filters including @repository, @Service, @Controller, @ManagedBean, @name, and so on.

Well, the above steps can be considered to be the complete initialization of the Spring container. To conclude, you have created a container containing the registry, bean factory, environment, important annotation postprocessor registered, and basic annotation filters registered, but the container is still incomplete. Because now there are no beans in the container.

Access to BeanDefinition

Going back to the container constructor, we continue with the scan method, which takes the package path we specified. From the literal meaning of this method, we know that this method scans the entire package path of the class and annotates the specified class into the bean container.

If we click on this method, we can see that the sacN method of the scanner object previously generated when the container was created is used to scan the specified package path.

We skip some trivial code and directly look at the implementation of the scanning part and directly look at the doScan method. There are three main parts of the logic here. The first step is to scan all the beanDefinitions of the specified annotation from the specified package path, and the second step is to do some post-processing for these bean definitions, including setting some properties. The third step is to check if the beanDefinition exists and if it does not then encapsulate beanDefinition definitionHolder in the registry.

For definitionHolder registration, there are two things that need to be done. The first step is to map the alias and the bean’s actual name to the aliasMap according to the bean configuration recorded in definitionHolder. The second thing is to put beanName and beanDefinition in a beanDefinitionMap and let beanName and definition do a mapping. BeanDefinition is an important concept in Spring. Spring uses class loading to get information about a class, and then encapsulates that information into a beanDefinition. Subsequent bean instances are instantiated according to that beanDefinition. I’ll explain more about this later in the bean declaration cycle section.

Let’s start by opening the findCandidateComponents() method to see how Spring puts the beanDefinition into the container during initialization.

In this case, the first step is to get the resources of all classes in the specified package path. Not all classes in the package path are to be placed in the bean container, so there are two criteria to filter all of these classes.

The first judgment in the code is for the class being examined. Remember the annotation filter, which determines whether the current class contains @Component? Of course, @controller, @service and other familiar annotations contain @Component annotations.

The second judgment mainly determines whether the current class is an interface or an abstract class and whether it is decorated by @lookup. If so, it is not placed in the container.

If we click on the first check method, we can see that it checks whether the current class’s annotations contain excluded annotations and returns false if they do, i.e. not added to the container. The next loop checks to see if the @Component annotation is included. The match method not only checks whether the current class contains the @Component annotation, but also whether the parent class or interface of the current class contains the @Component annotation if the current class does not. If so, it can be added to the container. I will not expand here, interested students can take a look at the source code. The isConditionMatch() method in the if judgment is used to handle Conditional injection of the @conditional annotation configuration.

The above section is the main process of scanning the specified package path when the container is initialized, and scanning the beans that need to be placed in the package path. To summarize, 1. Scan the specified file resources. 2. Check whether the specified file resource meets the conditions. The submitted beanDefinition is returned. 3. Do post-processing for these returned BeanDefinitions. 4. Place the beanDefinitionMap of the container.

refresh

The next step in the container initialization process is to refresh the container. This step makes various Settings for the container, and is sychronized and thread-safe.

In summary, there are mainly the following steps:

1. Prepare to refresh

2. Refresh and obtain beanFactroy

2. Prepare the beanFactory

3. Add different types of container-specific bean afterhandlers (hooks)

4. Add and execute the bean factory’s post-processor

5. Add bean backend handlers

6. International processing

7. Initialize event broadcast

8. Perform specific hooks for different types of containers

9. Register event listeners

10. Instantiate all beans that need to be instantiated

11. Complete the initialization

Ready to refresh

The refreshment preparation step is mainly to complete the verification of the system configuration. At the same time, as the beginning step of the whole container refreshment, the mark quantity will be set to mark the state of the container refreshment process, such as the closed and active mark quantity in the following figure. There is also a hook function for a particular container that provides the opportunity to add a particular configuration. The configuration files in Environent are then validated. Finally, the listener is initialized.

Refresh and get beanFactroy

In fact, this step is simply to get the beanFactory, but before getting the beanFactory in the container, the beanFactory refresh state will be set. Refreshed is a property of the AtomicBoolean type, where cas is used to try to change the state of the refresh, and if the refresh is successful, proceed with the whole process, If the current refreshed is true according to the current is the beanFactory by other threads to refresh it throws an exception: GenericApplicationContext type container does not support multiple threads to refresh.

Prepare the beanFactory

Now we’re going to set up the beanFactory that we’ve got, and we’re going to click on the prepareBeanFactory method and you can see that there are two important parts of this method, The first was to make the beanFactory added a rear ApplicationContextAwareProcessor processor, the CPU can for instance if the bean bean into some container level needed. Then call the next lot of ignoreDependencyInterface, this method is to tell when the bean is instantiated if this method need to inject the processed bean then won’t go to generate this depend on the bean directly, Instead, these objects are set into the object of the bean to be generated by the post-processor mentioned above.

Then you can also see the beanFactory also call redisterResolvableDependency method, this method is when the specified type to have multiple subclasses implement which bean can specify to get here.

The next step is to register some of the basic beans that the container runs with the beanFactory.

Add different types of container-specific bean afterhandlers (hooks)

After ready to play beanFacory Spring provides a template method is the hook function provided to inherit AbstractApplicationContext other containers, such as WebApplicationContext, etc., in the current class is an empty, By reading the comments on the method, we can see that this method provides the ability to register special bean posthandlers that subclasses need to add after the beanFactory in the container has completed its basic initialization and all bean definitions have been loaded but not instantiated.

Add and execute the bean factory’s post-processor

After completed the above steps, the spring container calls the invokeBeanFactoryPostProcessors method, This method is triggered in the code are put into container BeanDefinitionRegistryPostProcessor and spring BeanFactoryPostProcessor, after the trigger the post processor on the container map in the cache. Post-handlers that are fired and added to the cache are Ordered, processing @priorityordered annotations first and then @ordered annotations, as well as in the order in which their annotations were Ordered.

Add bean post-processor

After the bean registration post-handler and bean factory post-handler are completed, the bean creation post-handler is added. Spring call registerBeanPostProcessors method, the method of processing logic and the above steps in handling similar. The modified @Priorityordered postprocessors are registered before the modified @ordered postprocessors are processed, and the postprocessors are sorted according to the order in the annotations before being registered to ensure that the processors are executed in the same order. Note, however, that the container has not instantiated the bean yet, so these postprocessors do not execute here but register with the container and wait for the beanPostProcessor to be called by the process in the subsequent getBean method. A detailed explanation of the complex flow in the getBean method will follow in another article on the bean life cycle.

Internationalization processing

It doesn’t matter.

Initialize event broadcast

Ways of dealing with here is simply just the container applicationEventMulticaster assignment, namely from the container is a beanName applicationEventMulticaster a bean.

About ApplicationEventMulticaster this is actually a provide management the management of the Listener class in the spring.

And the execution of the Listener is handled by a built-in thread pool in Spring.

Perform specific processing (hooks) for different types of containers

As you can see from the annotation of the method, this provides different containers with their own unique handling mechanism for containers. By default, an empty implementation is a template method.

Register event listeners

This step registers all event handlers that implement the ApplicationListener interface with the container and executes the earlier event handlers. An explanation of the event handler Listener will also be covered in another article.

Instantiate all beans that need to be instantiated

This step is one of the key steps in refresh as a whole, and it instantiates all registered beans that are not lazily loaded throughout the container.

Instantiate the converter first, and then Aware. Any remaining beans that are not lazy-loaded are then instantiated. Freezing all BeanDefinitions prior to this step ensures that the bean definitions are not modified during the instantiation process.

Let’s click on the preInstantiateSingletons method, which is not long and easy to understand.

All beanName registered to the cache is first fetched from the previous cache, and these beans are then created in a loop. At creation time, the definition of the current bean and its parent class bean are merged, that is, the properties and methods in the parent class are merged. After that, the bean is not abstract, not lazy, and is a singleton. BeanName = beanName = beanName = beanName = beanName = beanName = beanName = beanName = beanName = beanName = beanName = beanName If so, call the getBean method to produce the factory bean (note that the instance factory bean is tinned, not the bean produced by the factory bean). Instantiate the bean directly if it is not a factory bean.

Once these beans have been initialized, it is necessary to execute the beanPostProcess registered and instantiated in the container to handle the beans that have just been instantiated. The newly created bean is first fetched from the first-level cache, known as the singleton cache, based on the beanName, and then triggered by the subsequent processor to complete the creation of the bean.

By default, there are only a few beans in the container to perform this step (ignoring the two beans that start with my that I created myself), most of which are post-processor and some configuration beans:

Complete initialization

The container is nearly finished updating at this point, and now it’s just a few finishing touches.

First, clean up some unnecessary caches to save memory space, then call the post-processor that declares the cycle, then publish an event that the container has started and completed, and finally register the container with a collection of containers.

Refresh the last

It is worth noting that if an exception is thrown in the above procedure, i.e. the container fails during initialization, the container will not stop working immediately, but will destroy all beans that were generated before the exception was thrown. It also clears caches generated during container startup that will not be used later.