This article takes you in-depth understanding of the IOC startup principle

This article takes you in-depth understanding of the IOC startup principle

1. IOC overview

1.1 What is it?

Two concepts: inversion of control, dependency injection

**Look at the traditional way of working: **Based on the single responsibility principle of an object, an object rarely completes its own work without relying on other objects, so at this time there will be dependencies between objects. And in our development, when we need any object, we will create what object, at this time the control of object creation is in our own hands. When too many objects are created, an object change will occur, and all the objects that depend on it have to be changed, which is highly coupled. At the same time as autonomy is manifested, there is also serious object coupling .

  • At this time, we will think about whether we can directly use this object when we use it, and give the ability to create the object to a third party, so that we don't need to care about how the object is created. About to hand over control of oneself. This is the inversion of control
  • At this time, there will be another problem, how can the object be directly used by us . When the object is created, we inject the object into this object, and then it can be used. This is dependency injection
  • **Another question, how is the coupling solved? **We only use this object through inversion of control. If the object is modified, we only need to modify the way the third party creates the object. Is there still so-called object coupling at this time?
  • Spring's family bucket address: Spring's latest family bucket information.

2. IOC architecture

A picture is done, this is the IOC's architectural idea, this is not its execution flowchart .

Let's interpret it step by step.

2.1 Vernacular

In the first chapter we learned that IOC is to help us manage and create objects .

At this time we need a container to carry the information we need to create, that is, the XML or annotations in the figure, then after we have our own BeanDefiniton information, we need an interface to read this information, so BeanDefinitionReader appears to read Our own Bean information.

Then we need to consider a question, how to produce so many objects?

The answer is the factory model. Spring's default factory is DefaultListableBeanFactory, yes, all objects in Spring (container objects and objects created by ourselves) are created by him. Mass production objects

At this time, there is another problem. We don't want to directly produce through BeanFactory. We need to perform some specific processing on this factory, so BeanFactoryPostProcessor appears, which is used to do some specific processing on the factory. We can customize BeanFactory by implementing this interface . Another brother said: I want to create some objects that I like separately, arrange , FactoryBean was born, it can help us create an object we need (the fourth part explains the difference between them in detail ).

Then another brother said: I want to make some special behaviors in my way before the unified object is created, simple, and arrange: see_no_evil

BeanPostProcessor appeared, and he provided two methods: one executes the internal Before method before the object is instantiated, and executes the After method after the initialization. ( Bean life cycle, detailed explanation of the fourth part )

At this time, some brothers have questions. Didn't it mean that the BeanPostProcessor is executed before the object is created? How is the Before method that is executed after the creation is completed.

If you guys have understood the concept of instruction reordering , you must have heard of a case where it takes three steps to create an object

  • Create space (instantiate)
  • initialization
  • Assignment

Among them, there will be instruction reordering during initialization and assignment

According to this point, it should be possible to get to a point, instantiation and initialization are not the same.

So it leads to another point, we have to perform some operations on the Bean, how to do it, it must be to modify the properties, or add some properties, etc., we need to wait for it to open up space in the heap, that is, to execute it after the instantiation is completed.

So the before method of BeanPostProcessor is executed after instantiation and before initialization.

After a lot of previous operations, finally our object entered our pocket (in the container).

Regarding destruction, under normal circumstances, we cannot get its destruction method through ApplicationContext, and can only be obtained through its subclasses. Regarding the destruction of the same process, first perform an operation before destruction, and then destroy it.

Among them, there will be instruction reordering during initialization and assignment

According to this point, it should be possible to get to a point, instantiation and initialization are not the same.

So it leads to another point, we have to perform some operations on the Bean, how to do it, it must be to modify the properties, or add some properties, etc., we need to wait for it to open up space in the heap, that is, to execute it after the instantiation is completed.

So the before method of BeanPostProcessor is executed after instantiation and before initialization.

After a lot of previous operations, finally our object entered our pocket (in the container).

Regarding destruction, under normal circumstances, we cannot get its destruction method through ApplicationContext, and can only be obtained through its subclasses. Regarding the destruction of the same process, first perform an operation before destruction, and then destroy it.

2.2 Actual work flow

Anyone who has read the Spring source code or heard it knows that there is a method called refresh, which has accomplished a lot of things. Of course, his behavior also represents the entire process of loading and instantiating objects in the IOC container. Let's take a closer look in the code interpretation of Chapter 3

Implementation process:

  • Load the configuration file, initialize the system environment Environment interface
  • Prepare the context and initialize some configuration resources
  • Create a factory
  • Add various environments to the factory
  • Get the BeanFactoryPostProcessor overridden by the subclass itself
  • Execution container and our own BeanFactoryPostProcessor
  • Register BeanPostProcessor
  • Internationalization
  • Broadcaster
  • Subclass initialization Bean
  • Registered listener, observer mode
  • Complete Bean creation
  • Publish the corresponding event, listener

3. Interpretation of IOC source code

3.1 Context configuration start

When ClassPathXmlApplicationContext was created, these methods were executed in the constructor.

To put it bluntly, a loader that parses the configuration file path is loaded; then the configuration file is obtained through the system environment variables, and some configuration files are removed from spaces, conversion expressions, etc. (without parsing); the last is the I marked it in red, and it did almost all the work in the refresh method. Let's talk in detail

3.2 refresh

This method almost completes all operations, creating factories, executing Processors, etc., instantiating objects, opening event monitoring, and so on.

Let's talk in detail

3.3.1 prepareRefresh()

The main function of this method is to do some preparatory work for the refresh of the application context. Check the resource file, set the startup time and active status, etc.

3.3.2 obtainFreshBeanFactory()

You can get it. It is mainly to create a factory BeanFactory, parse the configuration file, and load the Bean definition information (just answer this point during the interview, if you want to say, you can load the bean information below to chat) Yes, the ones marked in red are the points we will talk about next

This is the process of loading the configuration file. Note: There is still no analysis at this time, and the analysis is below the red one

This is the reading process. The specific analysis process comes from parse. This directly calls the class library for parsing XML in Java. If you are interested in reading it yourself, it finally returns a Document object.

Through the Document object, read the internal tags and execute different methods. The logic is the same as the idea of parsing the configuration file in MyBatis, so you can read it yourself.

At this time, all Bean definition information is saved to the BeanDefinitionRegistry interface, and then the registration method of the subclass DefaultListableBeanFactory factory is taken

3.3.3 prepareBeanFactory(beanFactory)

Prepare some environment for BeanFactory, which is convenient to use when instantiating, and add the container's own BeanPostProcessor

3.3.4 postProcessBeanFactory

Leave the BeanFactoryPostProcessor extended by subclasses,

3.3.5 invokeBeanFactoryPostProcessors(beanFactory)

This class involves two interfaces.

  • BeanFactoryPostProcessor
  • BeanDefinitionRegistryPostProcessor interface, this interface is a sub-interface of BeanFactoryPostProcessor, its priority is higher than BeanFactoryPostProcessor

Its overall execution process is: first execute BeanFactoryPostProcessor of BeanDefinitionRegistryPostProcessor, and then execute BeanFactoryPostProcessor

The following figure is the processing process of the BeanDefinitionRegistryPostProcessor interface

Processing logic of BeanFactoryPostProcessor

The overall logic is to classify first, skip the ones that have been processed, and classify the ones that have not been processed. The logic is the same as the above.

3.3.6 registerBeanPostProcessors

The logic of this method is the same as the above, except that the above is directly executed BeanFactoryPostProcessor, and this is only registered but not executed.

First get all the BeanPostProcessor type Beans in the factory, and then sort and process, sort and register.

3.3.7 initMessageSource()

Execute international content

3.3.8 initApplicationEventMulticaster

Created a multicast to provide support for adding Listeners.

Main logic:

  • Whether there is applicationEventMulticaster in the container, if it exists, register directly
  • If it does not exist, create a SimpleApplicationEventMulticaster and register it in the container.

3.3.9 onRefresh()

Subclass extension

3.3.10 registerListeners()

The realization of the observer pattern

3.3.11 finishBeanFactoryInitialization

There is too much content in this part, so I use code and diagrams to explain it.

The following figure is the main process of creating Bean

According to the serial numbers in the figure one by one:

  1. Whether BeanDefinition needs to be merged. BeanDefinition encapsulates the Bean into different Bean information definition classes according to different types of configuration file information. For example, the GenericBeanDefinition of our commonly used configuration file version; ScannedGenericBeanDefinition of the annotation scan version and so on.

And in this process, the parent definition and the child definition need to be merged when actually processing the definition information. There are mainly the following three aspects

  • There is parent definition information, use the parent definition information to create a RootBeanDefinition, and then pass in the custom information as a parameter.
  • There is no parent definition information, and the current BeanDefinition is of type RootBeanDefintion, directly return a clone of RootBeanDefintion
  • There is no parent definition information, and the current BeanDefintion is not of type RootBeanDefintiton, directly construct a RootBeanDefintion through the BeanDefintion and return

The above process is also the execution process in the source code

  1. isFactoryBean. Determine whether it is a FactoryBean

** Brief introduction: ** FactoryBean allows developers to create their own Bean interface. 3.methods are provided internally

When we use GetBean directly to the Bean, what we get is the Bean type specified by the factory to return. If you want to get the Bean itself, you need to get it with a prefix &

Let s look at one more point. This is the main method to get Bean from the container, and it is also the logic to solve the circular dependency.

Let s talk about how it solves circular references?

It introduces the concept of a three-level cache

When a circular reference occurs, it first creates the Bean through the ObjectFactory factory. **At this time, the object does not carry out attribute assignment, but only opens up space in the heap. **Then add the Bean at this time to the earlySingletonObjects container, which means that the Beans stored in this container are semi-finished products. **In the subsequent attribute assignment, since the object is a singleton, its reference address will not change, that is, the object is ultimately complete.

1. getBean. All objects are created directly through this method, which is also the core method of Spring. Let s take a look at its overall process.

Its main logic is:

  • First get the real name of the Bean to be instantiated, mainly to process the FactoryBean. After getting it, check whether the Bean has been created in the current container, and return directly if it exists.
  • If it does not exist, get its parent factory. If the parent factory is not empty and the current Bean information does not exist in the current container, try to get the Bean definition information from the parent factory to instantiate the Bean
  • If the parent factory is empty, the current Bean information is stored in the alreadyCreated cache.
  • Get the merged information of the current Bean (getMergedLocalBeanDefinition), check whether the current Bean has a dependency, if it exists, judge whether the current Bean and the dependent Bean are circular dependencies, if not, create the dependent Bean first
  • Determine the scope of the current Bean.
  • If the current Bean is a singleton object, create a Bean instance directly
  • If the current Bean is a multi-instance object, add the current Bean information to the multi-instance cache being created, and remove it after creation
  • If the current Bean is of other types, such as Requtst, Session, etc., customize an ObjectFacotry factory, override the getObject method, and create an object
  • After the object is created, judge whether the current object is the object you need, if it is returned directly; if it is not type conversion, if the type conversion fails, directly throw an exception

Next, take a look at the execution of CreateBean

The main thing this method accomplishes is: get the corresponding Class object by the name of the Bean; if the Class object obtained by the current Bean is not empty and the RootDefintiton can directly obtain the Bean, clone a copy of the Bean definition information for later use .

Verify the @Override information on the current Bean. Execute BeanPostProcessor, return a proxy object (if there is a proxy), if there is no proxy, create the Bean directly

Next, let s talk about this stuff- resolveBeforeInstantiation

Come on, go ahead and take a look at the preprocessor logic

The post processor did not look at it, it called all the post processors, and then executed it again, without other logic.

Next continue our topic: doCreateBean

The general process is as shown in the figure above:

First judge whether it is a singleton in the future, and then check whether there is a Bean being created from the FactoryBean cache, if it exists, take it out, if it does not exist, create an instance of the current Bean's wrapper class. Then get the instance and instance type of this class, and execute the post processor afterwards.

Whether the current Bean is a singleton, whether circular dependency is allowed, it is being created at the time, if it is, an ObjectFactory of the current Bean is created to solve the problem of circular dependency

Fill in the properties of the Bean and instantiate the Bean.

Check the early container cache (whether the bean is in the second-level cache in the cache). If so, it means that there is a circular dependency, and then deal with it

Let's look at circular dependencies first

Next, createBeanInstance

Spring provides three ways to create object packaging:

  • Directly created by the supplier object object. obtainFromSupplier
  • Directly created by the factory method.
  • Created by default. Whether the construction method needs to be automatically injected The construction method does not need to be automatically injected, and the default construction method is called

After this method is executed, one thing you should know is that the object instance has been created at this time, and the rest is to execute a series of enhancers and initialization methods, attribute filling, and so on.

We follow the order of code execution, the attribute filling is populateBean

This method executes logic:

  • First judge whether the incoming Bean is null, if it is null, judge whether there is an attribute value in the Bean definition information, if it exists, it is abnormal; if it does not exist, skip
  • Whether the current Bean definition information is after the merger, if it is and there are InstantiationAwareBeanPostProcessors in the factory at this time, then modify the Bean information before filling in the attributes
  • Get all the attribute values, analyze the automatic injection method of attribute values, Type or Name, and perform automatic injection
  • Determine whether there is InstantiationAwareBeanPostProcessors, modify the previously set attributes
  • Determine whether there is a dependency check, check the dependency
  • Attribute assignment

Next, look at the implementation of the initialization method, which is to call the BeanPostprocessor, init and other methods

This is the execution flow chart of this method. I believe that at this place, everyone should understand why the before method of BeanPostProcessor is executed in the init method. The function of this method is only for printing a life cycle, the object has been created before.


Next, let's look at the method of destruction. registerDisposableBeanIfNecessary

For singleton beans, Spring stores the beans that need to be destroyed in the disposableBeans cache, and encapsulates the destroyed beans through DisposableBeanAdapter

For other scopes, the destruction callback function is customized, but in the end it is encapsulated as DisposableBeanAdapter

In the process of encapsulating as DisposableBeanAdapter, it will first determine whether there is a destroy method in the Bean, and then assign a value to the destroyMethodName variable. Judge the parameters of this method again, if the number of parameters is greater than 1, an exception will be thrown

3.3.12 finishRefresh

This method performs a series of resource cleaning and

initLifecycleProcessor, this method is very simple, just look at whether there is a lifecycle processor in the current Bean, if it exists, use this directly, if it does not exist, create a default one and register it as a singleton and throw it into the container.