Here is my own understanding, so as to prevent it from being forgotten in the future. If there is a wrong understanding of that place, please point it out.

1. Background

In the process of writing code, we generally use @Autowired to inject another object, but sometimes it happens 循环依赖 , but our code does not report an error, what is the reason for this?

2. Preliminary knowledge

1. Consider the type of circular dependency

Here we consider the circular dependency of 单例 + @Autowired , without considering the injection of 构造器注入 or 原型作用域的Bean .

2. When is the proxy object created?

Bean创建的流程
Notice:
Under normal circumstances, that is, when there is no circular dependency, aop增强 is in the BeanPostProcessor#postProcessAfterInitialization method after the bean initialization is completed, but if there is a circular dependency, it needs to be in advance, getEarlyBeanReference Create proxy objects in advance.

3. What objects are stored in the L3 cache

cache field name cache level type of data explain
singletonObjects 1 Map<String, Object> What is saved is the complete Bean, that is, the Bean that can be used
earlySingletonObjects 2 Map<String, Object> The semi-finished Bean is saved, that is, the attribute has not been set, and the initialization work has not been completed.
singletonFactories 3 Map<String, ObjectFactory<?>> Mainly to generate beans, and then put them in the second level cache

Notice:
ObjectFactory#getObject() Each call will produce a new object or return the old one, depending on whether a proxy exists, etc.
ObjectFactory#getObject()

4. Get objects from level 3 cache

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)

从3级缓存中获取对象

5 Simplified creation process of Spring Bean

1. Instantiate a bean

 Object bean = instanceWrapper.getWrappedInstance();

Instantiate a Bean or new Bean()

2. Add to the L3 cache

 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

There are some conditions for adding to the third-level cache, and it is generally true. It is considered here that it needs to be added to the third-level cache.

3. Set the properties of the bean

 populateBean(beanName, mbd, instanceWrapper);

The first step instantiates the bean, but at this time the properties that need to be injected are not filled, and the properties are filled through this step.

4. Initialize beans

 Object exposedObject = initializeBean(beanName, exposedObject, mbd);

Initialize Bean, execute initialization method, Aware callback, execute BeanPostProcessor#postProcessAfterInitialization method ( aop的增强 is implemented in this)

If there is 循环引用 , the enhancement of aop needs to be advanced.

5. Add to the first level cache

 addSingleton(......)

3. Understanding

 @Component
class A {
    @Autowired
    private B b;
}

@Transaction (存在代理)
@Component
class B{
    @Autowired
    private A a;
}

1. Suppose that only singletonObjects and earlySingletonObjects can complete circular dependencies

cache field name cache level type of data explain
singletonObjects 1 Map<String, Object> What is saved is the complete Bean, that is, the Bean that can be used
earlySingletonObjects 2 Map<String, Object> The semi-finished Bean is saved, that is, the attribute has not been set, and the initialization work has not been completed.

At this time, it is necessary to obtain an instance of B 177fcd7210a476c0321f4c1a685f5fba---, that is, getBean("b") , which can be seen from the simplified process of Bean learned above.
SpringBean的简化流程
获取B的流程图
As can be seen from the above figure, 对象存在代理时 , the level 2 cache cannot solve the problem. Because the proxy object is done by BeanPostProcessor , 是在设置属性之后才产生的代理对象 .

At this point, some people may say, if I immediately perform Aop proxy after building the instance of B, won't this solve the problem? That assumes that there is no circular dependency between A and B, will this design be inelegant?

2. Suppose that only singletonObjects and singletonFactories can complete circular dependencies

获取B的流程图
It can be seen from the figure that it is not possible.

3. How to implement level 3 cache

1. Solve the proxy problem

Because by default, the proxy is completed by BeanPostProcessor , in order to solve the proxy, it is necessary to create the proxy in advance, then the creation of the proxy is placed in the level 3 cache for creation.

 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

getEarlyBeanReference This method will return the proxy bean

2. Solve the inconsistency of the value obtained by the singleton through the third-level cache multiple times

从3级缓存中获取对象
As can be seen from the above figure, the object is first searched from 一级->二级->三级缓存 . When the object is generated in the third-level cache, it is put into the second-level cache and cached, and the third-level cache is deleted at the same time.

3. Flowchart

获取B的流程图

4. Summary

1. The first level cache singletonObjects stores singletons that can be used.
2. The second-level cache earlySingletonObjects stores the early beans, that is, semi-finished products, which are still unavailable at this time.
3. The L3 cache singletonFactories is an object factory, which is used to create objects and put them into the L2 cache. At the same time, if the object has an Aop proxy, the object factory returns the proxy object.

Can the created proxy object be stored directly in earlySingletonObjects ? This can solve the problem, but the design may not be reasonable. Because in Spring Aop the proxy is created after the object is finished. And if there is no circular dependency, is it necessary to create proxy objects in advance? Divided into three-level cache, the code structure is clearer and more reasonable.


huan1993
218 声望34 粉丝

java工程师