[Frame source code] Bean creation source code process of Spring source code analysis

insert image description here

Question: How is a singleton bean initialized in Spring?

We all know that Spring parses the xml file described as BeanDefinition, parses BeanDefinition and finally creates Bean and puts Bean into the singleton pool, so what does Spring do in the process of creating Bean.

The most important method in the Spring core method refresh() is the finishBeanFactoryInitialization() method, which is responsible for initializing all singleton beans.

The finishBeanFactoryInitialization() method is located in step 11 of refresh().

insert image description here

At this point, all BeanFactories in the Spring container have been instantiated, that is, BeanFactoryPostProcessorthe beans that implement the interface have been initialized. The rest is initialization singleton beans. Most of our business beans are singletons. finishBeanFactoryInitializationThis step is to instantiate singletons and not set lazy-loaded beans.

Spring will finishBeanFactoryInitializationinitialize everything in this method singleton bean.

Ok, let's take a look at the logic inside the finishBeanFactoryInitialization method. The core of this method is to complete the BeanFactory configuration. This stage completes the instantiation of the context, including all singleton Bean objects that have been instantiated.

insert image description here

The core lies in the preInstantiateSingletons() method. The main task of the preInstantiateSingletons method is to initialize. Before initialization, it is also a series of judgments, such as whether it is lazy loaded, whether it is a factory bean (a special bean, responsible for the bean created by the factory), and finally Call the getBean() method.
insert image description hereinsert image description hereinsert image description here

The interface mentioned in the comment SmartInitializingSingletonis to let the bean do some operations after initialization.
insert image description here
insert image description here

OK, then the main method is still getBean()the method, the function of the getBean() method is to load and instantiate the Bean. The doGetBean() is called inside the method, let's look directly inside the **doGetBean()** method.
insert image description hereinsert image description hereinsert image description hereinsert image description hereinsert image description hereinsert image description hereinsert image description hereinsert image description here

There are a lot of codes, we mainly look at the createBean() method.
insert image description hereinsert image description hereinsert image description here

Let's continue to look inside the doCreateBean method.
insert image description hereinsert image description hereinsert image description hereinsert image description hereinsert image description hereinsert image description here

There are many methods, but we mainly need to focus on three methods.

  • createBeanInstance: instantiation, in fact, is to call the constructor of the object to instantiate the object
  • populateBean: fill in attributes, this step is mainly to fill in the dependency properties of multiple beans
  • initializeBean: Call the init method in spring xml.

From the singleton bean initialization steps described above, we can know that circular dependencies mainly occur in the first and second steps. That is, constructor circular dependencies and field circular dependencies.

Then we should start from the initialization process to solve the circular reference. For a singleton, there is one and only one object in the entire life cycle of the Spring container, so it is easy to think that this object should be stored in the Cache. In order to solve the singleton Circular dependency problem, using a three-level cache.

What is Spring's third-level cache?

The three-level cache in Spring's IOC container is a Map structure.

  • Level 1 cache (mature beans)
    • singletonObjectsThe singleton pool stores fully initialized beans, and the beans taken from the cache can be used directly
  • L2 cache
    • earlySingletonObjectsThe cache of the singleton object exposed in advance stores the original bean object (not yet filled with properties), which is used to solve the circular dependency
  • L3 cache
    • singletonFactoriesThe cache of the singleton object factory stores bean factory objects for resolving circular dependencies

insert image description here

OK, after understanding the third-level cache, let's look at the getSingleton() method. This method is mainly used to obtain the singleton Bean instance with the specified name from the singleton pool. The method internally implements the three-level cache search mechanism, and obtains the singleton Bean instance object with the specified name through the three-level search mechanism. At the same time, the method Synchronized code blocks are used to ensure thread safety in a multi-threaded environment.

insert image description here

OK, so here we have a question, why does Spring use a three-level cache to solve the problem of circular dependencies.

First of all, we have to make it clear that Spring can solve the dependency injection of the setter, but it cannot solve the dependency injection of the constructor.

If we now have an A object and a B object , a certain field or setter of A depends on the instance object of B , and at the same time a certain field or setter of B depends on the instance object of A ", this circular dependency situation.

A first completes the first step of initialization, and exposes itself to singletonFactories in advance. At this time, it performs the second step of initialization and finds that it depends on object B. At this time, it tries to get(B), and finds that B has not been created yet. , so go through the create process, B finds that he relies on object A during the first step of initialization, so he tries get(A), tries the first-level cache singletonObjects (certainly not, because A has not been fully initialized), and tries the second-level cache earlySingletonObjects (No), try the three-level cache singletonFactories, because A exposes itself in advance through ObjectFactory, so B can get the A object through ObjectFactory.getObject (although A has not been fully initialized, it is better than nothing), B takes After reaching the A object, the initialization phases 1, 2, and 3 have been successfully completed. After complete initialization, it will put itself into the first-level cache singletonObjects.

Returning to A at this time, A can get the object of B and successfully completes its own initialization phases 2 and 3. Finally, A also completes the initialization and enters the first-level cache singletonObjects, and more fortunately, because B got The object reference of A, so the A object that B now holds has completed the initialization.

insert image description here

When you know this principle, you must know why Spring can't solve the problem of " A's construction method relies on B's instance object, and B's construction method relies on A's instance object "! Because the premise of adding singletonFactories three-level cache is to execute the constructor, the circular dependency of the constructor cannot be resolved.

The following case will take you to experience the demonstration of constructor injection and set injection

Write class A class B

public class A {
    
    
    private B b;
    public A() {
    
    }
    public B getB() {
    
    
        return b;
    }
    public void setB(B b) {
    
    
        this.b = b;
    }
    public void method(){
    
    
        System.out.println("A方法调用");
    }
}
public class B {
    
    
    private A a;
    public B(){
    
    }
    public A getA() {
    
    
        return a;
    }
    public void setA(A a) {
    
    
        this.a = a;
    }
    public void method(){
    
    
        System.out.println("B方法调用");
    }
}

write xml

    <bean id="b" class="com.lixiang.demo.B">
        <property name="a" ref="a"></property>
    </bean>
    <bean id="a" class="com.lixiang.demo.A">
        <property name="b" ref="b"></property>
    </bean>

test

insert image description here

Modify the xml configuration

    <bean id="b" class="com.lixiang.demo.B">
        <!--将a改成用构造器注入-->
        <constructor-arg name="a" ref="a"></constructor-arg>
    </bean>
    <bean id="a" class="com.lixiang.demo.A">
        <property name="b" ref="b"></property>
    </bean>

code adjustment

public class B {
    
    
    private A a;
    public B(A a){
    
    
        this.a = a;
    }
    public A getA() {
    
    
        return a;
    }
    public void setA(A a) {
    
    
        this.a = a;
    }
    public void method(){
    
    
        System.out.println("B方法调用");
    }
}

test

insert image description here

It can be found that injecting with the constructor is abnormal.

Spring introduces the mechanism of "exposing beans in advance". When creating an object A, it will first create an empty object of A and add it to the cache pool.

That is, "expose the Bean in advance", and then continue to create the B object and inject it into the A object. When creating the B object, since the A object is already in the buffer pool, the A object can be obtained directly, and then the B object is injected into the A object to complete the Bean initialization.

insert image description here

Ok, now the entire Bean creation process has been completed. We are looking at the specific implementation of the following three methods.

First of all, the first one is the createBeanInstance() method
insert image description hereinsert image description hereinsert image description here

Then the populateBean() method
insert image description hereinsert image description hereinsert image description hereinsert image description here

Finally, the initializeBean() method
insert image description hereinsert image description here

Ok, so far the creation process of Spring beans has been sorted out.

Remember to like + follow!

insert image description here

Guess you like

Origin blog.csdn.net/weixin_47533244/article/details/131213407