Didn't it mean that Spring solved the circular dependency problem-why the project still reported the circular dependency exception

what? The SpringBoot project starts reporting a circular dependency exception

Today, I started the project and reported a cyclic dependency exception. To illustrate, I simplified it here.

My code is like this (simulated)

@Component
public class TestA {
    @Autowired
    private TestB testB;

    @Async("taskExecutor")
    public TestB getTestB(){
        return testB;
    }
}
@Component
public class TestB {
    @Autowired
    private TestA testA;

    public void testTrans() {
        testA.getTestA();
        System.out.println("testTrans线程名称:" + Thread.currentThread().getName());
    }
}

Here, TestA calls TestB, and TestB calls TestA. It is a typical cyclic dependency scenario, but we know that Spring handles the cyclic dependency problem. But why is there an error here?

Circular dependency association

To this end, let's analyze how Spring solves the circular dependency problem.

Cyclic dependency

Let's take a look at which situations are circular dependencies.

The creation of object M depends on the creation of S, and the creation of S depends on the creation of M.

Circular dependency definition 1

The creation of object M depends on the creation of S, the creation of S depends on the creation of O, and the creation of O depends on the creation of M. This also forms a closed loop.

Circular dependency definition 2

There are also self-reliance.

Circular dependency definition 3

Bean instantiation process

Let's take a look at how the Spring container obtains the bean process.

First enter the getBean()method, the doGetBean()method is called inside .

doGetBean()In the method, the method is called getSingleton()to obtain the sharedInstanceinstance.

getSingleton()Methods as below:

This method is mainly to obtain the singleton of the bean from the cache. It will be judged here that if the single column currently to be obtained has a circular dependency, it will be obtained from other caches. Let's first take a look at which caches the bean instance is mainly obtained from.

  • singletonObjects

  • earlySingletonObjects

  • singletonFactories

The cached data structure is map:

So what are these caches for?

First we need to understand the steps of bean initialization

bean initialization

singletonObjects: Fully initialized objects are stored in the secondary map

earlySingletonObjects: Early instantiation of an object means that the object has opened up space in memory, but has not filled the properties inside. This cache is the second-level cache, used to resolve circular dependencies.

singletonFactories: After the attribute is filled, before the initialization. If early exposure is allowed, the instantiated bean will be added to this cache, here is what we call the third-level cache.

So, how is the instance obtained from the cache?

  • singletonObjects Get the instance first  , singletonObjects the instances in are all prepared bean instances and can be used directly;

  • If the retrieved instance is found to be null or is being created, go to the secondary cache to obtain it;

  • If not in the second-level cache, get it from the third-level cache;

  • If it is in the third-level cache, move it to the second-level cache;

  • If the third-level cache is not available, null is returned directly.

Object sharedInstance = getSingleton(beanName);

When the third-level cache returns null, the sharedInstancevalue is null. The bean will be created at this time.

The method of creating a bean is as follows:

createBean(beanName, mbd, args);

createBean()The method calls the following method

Object beanInstance = doCreateBean(beanName, mbdToUse, args);

Will execute inside

instanceWrapper = createBeanInstance(beanName, mbd, args);

The next call is addSingletonFactoryto add the bean to the singletonFactories cache (three-level cache).

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

Execute nextpopulateBean()

populateBean(beanName, mbd, instanceWrapper);

populateBean()It is mainly to fill in the properties of the early bean. If it is found that bean A needs to rely on bean B when filling the properties, the B object will be created. At this time, it is found that B needs to rely on A when filling the attribute value, and it will obtain the bean A that has not yet filled the attribute value from the third-level cache. At this time, the original reference of the A object can be injected into the B object (and moved to the second-level cache) to solve the circular dependency problem. At this time,  getObject() even if the execution of the method ends, it returns the fully instantiated bean.

Finally, the fully instantiated bean object is stored in the first level cache singletonObjects.

Let's take a look at the flow chart of obtaining beans:

Cyclic dependency resolution diagram

It can be seen from the above that when there is a sequential loop dependency, the beans that are exposed early are obtained from the third-level cache and then moved to the second-level cache.

Can only the secondary cache solve the circular dependency problem?

At this time, are you in doubt, why do you need a three-level cache? Can the secondary cache solve the circular dependency problem?

Let's look at this code

If the obtained object is a proxy object, singletonFactory.getObject()a new proxy object will be generated here . In order to ensure that there is only one proxy object, a cache is needed to cache the proxy object.

So back to the question at the beginning of the article @Async, why does Spring not solve the circular dependency problem for beans with annotated methods?

In fact @Async, the proxy does not support you to circular references by default, because it does not provide early references of proxy objects. @AsyncThe proxy object is not getEarlyBeanReference()created in, but postProcessAfterInitializationthe proxy is created.

Then you may be @Transactionalwondering whether the implementation of annotations is @Asyncconsistent, and whether there will be circular dependencies. The answer is not to appear, because @Transactionalthe automatic proxy creator is used AbstractAutoProxyCreator, which implements the getEarlyBeanReference()method to provide good support for circular dependencies.

In addition @Async, the @Validatedsame problem also occurs.

Recommended in the past

Scan the QR code to get more exciting. Or search Lvshen_9 on WeChat , you can reply to get information in the background

1.回复"java" 获取java电子书;

2.回复"python"获取python电子书;

3.回复"算法"获取算法电子书;

4.回复"大数据"获取大数据电子书;

5.回复"spring"获取SpringBoot的学习视频。

6.回复"面试"获取一线大厂面试资料

7.回复"进阶之路"获取Java进阶之路的思维导图

8.回复"手册"获取阿里巴巴Java开发手册(嵩山终极版)

9.回复"总结"获取Java后端面试经验总结PDF版

10.回复"Redis"获取Redis命令手册,和Redis专项面试习题(PDF)

11.回复"并发导图"获取Java并发编程思维导图(xmind终极版)

Another: Click [ My Benefits ] to have more surprises.

Guess you like

Origin blog.csdn.net/wujialv/article/details/108990015
Recommended