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().
At this point, all BeanFactories in the Spring container have been instantiated, that is, BeanFactoryPostProcessor
the beans that implement the interface have been initialized. The rest is initialization singleton beans
. Most of our business beans are singletons. finishBeanFactoryInitialization
This step is to instantiate singletons and not set lazy-loaded beans.
Spring will finishBeanFactoryInitialization
initialize 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.
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.
The interface mentioned in the comment SmartInitializingSingleton
is to let the bean do some operations after initialization.
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.
There are a lot of codes, we mainly look at the createBean() method.
Let's continue to look inside the doCreateBean method.
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)
singletonObjects
The singleton pool stores fully initialized beans, and the beans taken from the cache can be used directly
- L2 cache
earlySingletonObjects
The 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
singletonFactories
The cache of the singleton object factory stores bean factory objects for resolving circular dependencies
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.
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.
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
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
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.
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
Then the populateBean() method
Finally, the initializeBean() method
Ok, so far the creation process of Spring beans has been sorted out.
Remember to like + follow!