Hey, guys, this is the classic chicken wing. Today, brother wings would like to talk to you about Spring’s cyclic dependency. After reading this article, you will meet!

What are circular dependencies

Multiple beans depend on each other, forming A closed loop for example: A depends on B, B depends on C, and C depends on A.

public class CircularDependency { class A { B b; } class B { C c; } class C { A a; }}Copy the code

In general, if you ask the Spring container how loop dependencies are handled internally, you are referring to the default singleton Bean, where properties reference each other. In other words, Spring’s cyclic dependencies are a problem with Spring container injection.

The impact of two injection modes on cyclic dependencies

The official website explains circular dependencies

The impact of two injection modes on cyclic dependencies

Constructor injection: Easy to create irresolvable cyclic dependencies, (If you use constructor injection, It is possible to create an unresolvable circular dependency scenario.)

Setter injection: Setter injection of singleton beans is recommended

Conclusion: We do not have A cyclic dependency problem as long as A is A setter and singleton

The Spring container loop relies on exceptions

Understand loop dependencies through code

There are two ways to inject dependent objects into the Spring container.

Constructors inject dependencies

code

1, ServiceA

@Component public class ServiceA { private ServiceB serviceB; public ServiceA(ServiceB serviceB) { this.serviceB = serviceB; }}Copy the code

2, ServiceB

@Component public class ServiceB { private ServiceA serviceA; public ServiceB(ServiceA serviceA) { this.serviceA = serviceA; }}Copy the code

3, ClientConstructor

/** * inject a dependency into a bean using a constructor. */ public class ClientConstructor {public static void main(String[] args) {new ServiceA(new ServiceB(new)  ServiceA(new ServiceB()))); . }}Copy the code

Conclusion: Constructor injection does not solve circular dependencies. You want constructor injection to support circular dependencies, which do not exist. If the constructor can solve the loop dependency problem, then I can do infinite nesting ~

Visual understanding: Each instantiation requires the other instance, which is similar to a deadlock, if there is no solution, then they will wait for each other forever

Setter injection

code

1, ServiceA

@Component public class ServiceA { private ServiceB serviceB; public void setServiceB(ServiceB serviceB) { this.serviceB = serviceB; System.out.println("A set B"); }}Copy the code

2, ServiceB

@Component public class ServiceB { private ServiceA serviceA; public void setServiceA(ServiceA serviceA) { this.serviceA = serviceA; System.out.println("B set A"); }}Copy the code

3, ClientConstructor

Public class ClientSet {public static void main(String[] args) {// Create serviceA serviceA serviceA = new serviceA (); // Create serviceB serviceB = new serviceB (); // Inject the serviceA into the serviceB. // Inject the serviceB into the serviceA. }}Copy the code

Conclusion: Setters can solve the problem of loop dependency

Demonstrate loop dependency exceptions

Class A

public class A { private B b; public B getB() { return b; } public void setB(B b) { this.b = b; } public A() { System.out.println("---A created success"); }}Copy the code

Class B

public class B { private A a; public A getA() { return a; } public void setA(A a) { this.a = a; } public B() { System.out.println("---B created success"); }}Copy the code

ClientSpringContainer class

public class ClientSpringContainer { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); A a = context.getBean("a", A.class); B b = context.getBean("b", B.class); }}Copy the code

Create the applicationContext.xml file in the Resources folder to inject the properties in the bean

<? The XML version = "1.0" encoding = "utf-8"? > <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <! - 1. The spring container default singleton pattern can solve the circular reference, singleton support 2. By default the spring container prototype dependence pattern scope = "prototype" cannot solve many cases mode circular reference -- > <! --> <! <! - scope = "prototype" is for every time a new object - > < bean id = "a" class = "com. Heygo. Spring. Circulardependency. A" > < property name = "b" ref="b"/> </bean> <bean id="b" class="com.heygo.spring.circulardependency.B"> <property name="a" ref="a"/> </bean> </beans>Copy the code

Scope = “singleton”, the default singleton scenario is to support cyclic dependency, no error.

Singleton, beanA, and beanB are created successfully without throwing an exception.

Scope = “prototype”, prototype (prototype) scenarios do not support loop dependency, error

Change the bean’s life cycle to Prototype.

Oh oh, throw exceptions: the Exception in the thread “is the main” org. Springframework. Beans. Factory. BeanCreationException: Error creating bean with name ‘a’ defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean ‘b’ while setting bean property ‘b’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘b’ defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean ‘a’ while setting bean property ‘a’; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘a’: Requested bean is currently in creation: Is there an unresolvable circular reference?

Circular dependency solutions

Important conclusion: Spring internally addresses loop dependencies through level 3 caching.

So-called triple cache is the Spring container used to solve the problem of circular dependencies within three Map, the three Map in DefaultSingletonBeanRegistry class.

The first level of cache: Map<String, Object> singletonObjects, which I would like to call the finished singleton pool, is often referred to as the Spring container, which is where we get singleton beans, storing bean objects that have gone through the full life cycle.

Level 2 cache: Map<String, Object> earlySingletonObjects, which stores Bean objects that have been exposed earlier than the end of the Bean’s life cycle.

Level 3 cache: Map<String, ObiectFactory<? >> singletonFactories, which store factories that can generate beans and use them to produce (create) objects.

Source code Deug pre-knowledge

Instantiation & initialization

The difference between instantiation and initialization instantiation: a block of memory is allocated in heap memory

Initialization: Completes the population of the property

Three maps and four methods

Level 3 cache + four methods

Three levels of cache

Level 1 cache: Stores beans that have already been initialized, with Bean names corresponding to Bean instances, known as a singleton pool. Represents a Bean object that has gone through a full life cycle.

Level 2 cache: stores beans that are instantiated but not initialized, with Bean names corresponding to Bean instances. Indicates that the Bean has not completed its life cycle (the Bean’s properties have not been populated) and is stored in the cache. That is, beans that are instantiated but not initialized are placed in the cache.

Level 3 cache: Represents the factory where the generated beans are stored. Factorybeans are stored. The bean name corresponds to the bean factory. If class A implements A FactoryBean, then the dependency injection time is not class A, but the Bean generated by class A.

The four methods

GetSingleton () : Obtains the singleton bean from the container, or creates the bean if it does not.

DoCreateBean () : Performs the bean creation operation (methods that start with do in Spring are practical methods).

PopulateBean () : After the bean is created, it populates the bean’s properties.

AddSingleton () : After the bean is initialized, it is added to the singleton container pool and retrieved the next time the getSingleton() method is executed.

Map<String, ObjectFactory<? SingletonFactories (Value) is an instance of the ObjectFactory interface implementation class. ObjectFactory is a functional interface in which a getObject() method is defined to get beans, which is the embodiment of the factory idea (factory design pattern).

Object migration in level 3 cache

Description of the migration of A/B objects in the three-level cache.

B is needed during the creation of A, so A instantiates B by putting itself in A level 3 cache.

When B instantiates, it finds that A is needed, so B first checks level 1 cache, no, then checks level 2 cache, or no, then checks level 3 cache, finds A, and then puts the A in level 3 cache into level 2 cache, and deletes the A in level 3 cache.

B is successfully initialized, put itself in level 1 cache (A in B is still being created), and then go back and create A again. At this point, B has been created, get B directly from level 1 cache, and then complete the creation, and put itself in level 1 cache.

Detailed Debug Process

Instantiation of beanA

In the ApplicationContext context = new ClassPathXmlApplicationContext (” the applicationcontext.xml “); Code on the break, and gradually implement (Step Over), found that execute new ClassPathXmlApplicationContext (” the applicationcontext.xml “), and beanA beanB have been created, So we need to enter the new ClassPathXmlApplicationContext (” the applicationcontext.xml “).

Enter the new ClassPathXmlApplicationContext (” the applicationcontext.xml “).

Click Step Into to enter the static code block first. For our business, use Step Out to exit the method.

Step Into, again Into the ClassPathXmlApplicationContext constructor of a class, the constructor to use this call another overloaded constructors.

We Step Into the overloaded constructor and Step Over. After executing the refresh() method, we find the following log output, so we put the breakpoint on the refresh() line.

Enter the refresh() method.

Step Into Into the refresh () method, found that after finishBeanFactoryInitialization output log after (the beanFactory) method, So we will play in the breakpoint finishBeanFactoryInitialization (the beanFactory) that line.

It can also be seen from the comments that this method instantiates all Remaining (non-lazy-init) singletons.

Enter finishBeanFactoryInitialization (the beanFactory) method.

Step Into entering finishBeanFactoryInitialization (the beanFactory) method, found that after the beanFactory. PreInstantiateSingletons output log after () method, So we will play in the beanFactory breakpoint. PreInstantiateSingletons () the line.

It can also be seen from the comments that this method instantiates all Remaining (non-lazy-init) singletons.

Enters the beanFactory. PreInstantiateSingletons () method.

Step Into Into the beanFactory. PreInstantiateSingletons () method, found that the execution of the getBean (beanName) method after the log output, so we will play in the breakpoint getBean (beanName) that line.

Enter the getBean(beanName) method.

GetBean(beanName) calls the doGetBean(Name, NULL, NULL, false) method.

Enter the doGetBean(Name, NULL, NULL, false) method.

We can alias the bean, where the transformedBeanName(name) method translates the user alias to the bean’s real name.

Enter the getSingleton(beanName) method.

It’s worth talking about the getSingleton(beanName) method.

Call its overloaded method allowEarlyReference == true to get the bean from the tertiary cache earlySingletonObjects, AllowEarlyReference == false indicates that beans cannot be fetched from the tertiary cache earlySingletonObjects.

The getSingleton(beanName, true) method attempts to fetch beanA from the level-1 cache singletonObjects, BeanA haven’t started yet (isSingletonCurrentlyInCreation (beanName) returns false), get less than returns null.

Return to the doGetBean(Name, NULL, NULL, false) method.

The getSingleton(beanName) method returns NULL.

What we mean by beans to Spring is one RootBeanDefinition instance after another.

This dependsOn variable corresponds to the bean depends-on=”” property, which we have not configured and is therefore null.

After going around and finding that there is no beanA, I’m finally ready to create beanA.

GetSingleton (beanName, () -> {… }.

In IDEA 2020, clicking Step Into manually selects the method to enter, so we need to use the left mouse button to click on the getSingleton() method.

Create beanA from singletonObjects (singletonObjects == null) Creating shared instance of singleton bean ‘A’

When performing the singletonObject = singletonFactory. GetObject (); , the output will be “- A created success], suggesting that perform singletonFactory. GetObject () method will be instantiated beanA, and according to the code variable names can be learned that create singleton factory, This singleton factory is the Lambda expression we passed in.

Enter the createBean(beanName, MBD, args) method.

We Step Into the createBean(beanName, MBD, args) method, where mbdToUse will be used to create beanA.

DoCreateBean (beanName, mbdToUse, args) to instantiate beanA

Enter the doCreateBean(beanName, mbdToUse, args) method.

Step Into doCreateBean(beanName, mbdToUse, args). FactoryBeanInstanceCache does not have beanA Wrapper cache. InstanceWrapper == null, so we need to create beanA’s corresponding instanceWrapper. BeanA instantiation goes through an instanceWrapper, which wraps the beanA instance.

Enter the createBeanInstance(beanName, MBD, args) method.

This looks like reflection.

Shortcut when re-creating the same bean… Resolved == true as soon as this bean is instantiated.

Candidate constructors for autowiring? Is the constructor auto-injected? Call the instantiateBean(beanName, MBD) method on return to instantiate the beanA and return it.

Enter the instantiateBean(beanName, MBD) method.

The getInstantiationStrategy().instantiate(MBD, beanName, this) method completes the beanA instantiation.

Enter the getInstantiationStrategy().instantiate(MBD, beanName, this) method

First get has been parsed constructor bd. ResolvedConstructorOrFactoryMethod, this is the first time, certainly has not, so constructorToUse = = null. Then get the type of A and throw an exception if it is found to be an interface. Finally get A public constructor, and its assignment to bd. ResolvedConstructorOrFactoryMethod.

The purpose of obtaining the constructor is, of course, to instantiate beanA.

Enter the BeanUtils. InstantiateClass (constructorToUse) method.

Create a beanA instance from the beanA constructor, Step Over: Instantiate (MBD, beanName, this). Instantiate (MBD, beanName, this)

Return to the getInstantiationStrategy().instantiate(MBD, beanName, this) method.

In BeanUtils. InstantiateClass (constructorToUse) method created beanA examples, but has not been initialized, you can see attribute b = null, Step Over will return to the instantiateBean(beanName, MBD) method.

Return to the instantiateBean(beanName, MBD) method.

Gets the instance of beanA you just created, but its properties are not initialized.

Load the instantiated beanA into BeanWrapper and return BW.

Return to the createBeanInstance(beanName, MBD, args) method.

Get the beanWrapper instance you just created, which wraps the beanA instance you just created.

Go back to the doCreateBean(beanName, mbdToUse, args) method.

The BeanWrapper instanceWrapper is obtained in the doCreateBean(beanName, mbdToUse, args) method to encapsulate the beanA instance.

Gets and records the full class name of A.

Perform BeanPostProcessor

If the bean is a singleton bean (MBD isSingleton ()), and allows circular dependencies (enclosing allowCircularReferences), And the current bean is created in the process of (isSingletonCurrentlyInCreation (beanName)), then allows early exposed the singleton bean (earlySingletonExposure = true), AddSingletonFactory (beanName, () -> getEarlyBeanReference(beanName, MBD, The bean)) method puts the bean in a level-three cache singletonFactories.

Enter the addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, MBD, bean)) method.

Add beanA to singletonObjects (” singletonObjects “). Remove beanA from the secondary cache earlySingletonObjects, and finally add beanName to registeredSingletons to indicate that the bean instance has been registered.

BeanA property population

Go back to the doCreateBean(beanName, mbdToUse, args) method.

Then back to the doCreateBean(beanName, mbdToUse, args) method, the populateBean(beanName, MBD, instanceWrapper) method needs to be executed to populate the properties in beanA.

Enter the populateBean(beanName, MBD, instanceWrapper) method.

Gets a list of beanA attributes.

Execute applyPropertyValues(beanName, MBD, BW, PVS) to fill in the beanA property.

Go to the applyPropertyValues(beanName, MBD, BW, PVS) method.

Get beanA’s attribute list and find an attribute B.

Traverse each attribute, and the injection of each attribute, valueResolver. ResolveValueIfNecessary (pv, originalValue) : Given a PropertyValue, return a value, resolving any references to other beans in the factory if necessary.

Enter valueResolver. ResolveValueIfNecessary (pv, originalValue) method.

Resolve dependency injection by resolveReference(argName, ref).

Enter the resolveReference(argName, ref) method.

Get the name of property B, and then get an instance of beanB using the this.beanFactory.getBean(resolvedName) method.

Instantiation of beanB

Go to the this.beanFactory.getBean(resolvedName) method.

Oh, the familiar doGetBean(Name, NULL, NULL, false) method, and that’s where the recursion begins.

Execute the doGetBean(Name, NULL, NULL, false) method again.

BeanB has not yet been instantiated, so the getSingleton(beanName) method returns NULL.

CreateBean () = beanB; createBean() = beanB;

GetSingleton (beanName, () -> {… }.

First try to fetch beanB from the level 1 cache singletonObjects.

Then call singletonFactory. GetObject () create beanB.

Enter the createBean(beanName, MBD, args) method.

The type of access to the beanB for com. Heygo. Spring. Circulardependency. B.

Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. BeanPostProcessors a chance to return a proxy instead of the target bean instance. That is, we can use BeanPostProcessors to return the bean’s proxy, not the bean itself. Then came the doCreateBean(beanName, mbdToUse, ARGS) segment.

Enter the doCreateBean(beanName, mbdToUse, args) method. As usual, create BeanWrapper instanceWrapper for beanB.

Enter the createBeanInstance(beanName, MBD, args) method.

Call instantiateBean(beanName, MBD) to create a beanWrapper.

Enter the instantiateBean(beanName, MBD) method.

Call getInstantiationStrategy().instantiate(MBD, beanName, this) to create a beanWrapper.

Enter the getInstantiationStrategy().instantiate(MBD, beanName, this) method.

Access com. Heygo. Spring. Circulardependency. B’s constructor, and the constructor information recorded in bd. ResolvedConstructorOrFactoryMethod field.

Call BeanUtils. InstantiateClass (constructorToUse) method to create beanB instance.

Enter the BeanUtils. InstantiateClass (constructorToUse) method.

Create an instance of beanB by calling the constructor of class B, at which point the console prints: [-b created Success].

Return to the instantiateBean(beanName, MBD) method.

InstantiateBean (beanName, MBD) method to get the created beanB instance and drop it into beanWrapper, encapsulating it as a beanWrapper BW object.

Go back to the doCreateBean(beanName, mbdToUse, args) method.

The createBeanInstance(beanName, MBD, args) method returns the beanWrapper enclosing beanB.

Run the BeanPostProcessor process.

Because beanB satisfies the singleton and is being created, beanB can be exposed ahead of time (before the property is initialized), The addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, MBD, bean)) method is called to add it to the level-3 cache singletonFactory.

Enter the addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, MBD, bean)) method.

Add the beanB instance to the level-3 cache singletonFactory, remove it from the level-2 cache earlySingletonObjects, and register its beanName.

Go back to the doCreateBean(beanName, mbdToUse, args) method.

Perform populateBean (beanName, MBD, instancewrapper) method fill the beanB attributes.

BeanB property population

Enter the populateBean(beanName, MBD, instanceWrapper) method.

Execute the mbd.getPropertyValues() method to get the property list of beanB.

Execute applyPropertyValues(beanName, MBD, BW, PVS) to complete the filling of the beanB property.

Go to the applyPropertyValues(beanName, MBD, BW, PVS) method.

Perform MPVS. GetPropertyValuelist () method to obtain a list of attributes for the beanB.

Traverse each attribute, and the injection of each attribute, valueResolver. ResolveValueIfNecessary (pv, originalValue) : Given a PropertyValue, return a value, resolving any references to other beans in the factory if necessary.

Enter valueResolver. ResolveValueIfNecessary (pv, originalValue) method.

Execute the resolveReference(argName, ref) method to inject the attribute named A into the beanB.

Enter the resolveReference(argName, ref) method.

GetBean(resolvedName); getBean(name, null, null, false);

Enter the doGetBean(Name, NULL, NULL, false) method.

GetSingleton (beanName) will get the beanA instance. The answer is yes.

Enter the getSingleton(beanName, true) method.

GetSingleton (beanName) calls its overloaded method getSingleton(beanName, true).

BeanA is not stored in the cache singletonObjects, so the execution Object singletonObject = this. SingletonObjects. After the get (beanName), SingletonObject = = null, plus beanA are created conditions (isSingletonCurrentlyInCreation (beanName) = = true), so if can enter the first layer of judgment.

BeanA is stored in the level-three SingletonObjects cache, and null is fetched from the level-two earlySingletonObjects cache, so it can be used for the level-two if determination.

Fetching beanA from level 3 cache is definitely not empty, so we can proceed to level 3 if.

① Obtain beanA from singletonFactory; ② Add beanA to level 2 cache earlySingletonObjects; ③ Remove beanA from singletonFactories.

Return to the doGetBean(Name, NULL, NULL, false) method.

Executing Object sharedInstance = getSingleton(beanName) fetches the beanA that was stored in the singletonFactories level3 cache.

Boy, I got beanA and went straight back.

Go back to the applyPropertyValues(beanName, MBD, BW, PVS) method.

Perform valueResolver. ResolveValueIfNecessary (pv, originalValue) method to obtain beanA instance.

Add the attribute beanA to the deepCopy collection (List deepCopy = new ArrayList<>(original.size())).

Executing bw.setPropertyValues(New MutablePropertyValues(deepCopy)) will populate the A property in beanB.

Go to the bw.setPropertyValues(New MutablePropertyValues(deepCopy)) method.

Its overloaded method setPropertyValues(PVS, false, false) is called.

Go to the setPropertyValues(PVS, false, false) method.

In this method, each property of the bean is populated (the property is assigned by the setPropertyValues(PVS, false, false) method).

Go back to the applyPropertyValues(beanName, MBD, BW, PVS) method.

Bw. setPropertyValues(New MutablePropertyValues(deepCopy)) assigns the elements in deepCopy to the properties of beanB. The a attribute in beanB has been assigned to beanA.

Go back to the doCreateBean(beanName, mbdToUse, args) method.

Since instanceWrapper encapsulates beanB, after executing the populateBean(beanName, MBD, instanceWrapper) method, BeanB contains beanA, but beanA does not contain beanB.

The getSingleton(beanName, false) method is passed in allowEarlyReference = false to disallow fetching beanB from the singletonFactories level-three cache.

Enter the getSingleton(beanName, false) method.

The singletonObject is null because the beanB is stored in the singletonFactories.

Go back to the doCreateBean(beanName, mbdToUse, args) method.

This should be the destroy-method that executes the bean, and its internal logic should only execute if the bean is a singleton at factory destruction time. RegisterDisposableBeanIfNecessary (beanName, bean, MBD) method of the comments are as follows: Add the given bean to the list of disposable beans in this factory, registering its DisposableBean interface and/or the given destroy method to be called on factory shutdown (if applicable). Only applies to singletons. Finally, return beanB (attribute A has been populated).

Return to the createBean(beanName, MBD, args) method.

Execute the doCreateBean(beanName, mbdToUse, args) method to get the wrapped beanB instance (attribute A is already populated) and return it.

Return to getSingleton(beanName, () -> {… } method.

Perform singletonFactory. GetObject () method to get to the beanB, before the singletonFactory here is call getSingleton (beanName () – > {… } method passes in a Lambda expression, and then sets newSingleton to true.

Execute the addSingleton(beanName, singletonObject) method to add the beanB instance to the level 1 cache singletonObjects.

Enter the addSingleton(beanName, singletonObject) method.

① Place the beanB in the level-1 cache singletonObjects

Delete the beanB from the singletonFactories cache.

③ Remove beanB from earlySingletonObjects (beanB is not in earlySingletonObjects)

Register beanB’s beanName with registeredSingletons

Return to getSingleton(beanName, () -> {… } method.

The beanB is returned after addSingleton(beanName, singletonObject) is executed to add the beanB to the first-level cache singletonObjects.

Return to the doGetBean(Name, NULL, NULL, false) method.

GetSingleton (beanName, () -> {… } method to get the beanB with the attributes already populated and added to the level-1 cache singletonObjects.

I’m going to return beanB, and I’m going to return where? BeanB was created because beanA wanted to populate its property B, so it must return beanB to beanA.

BeanA property population

Return to the resolveReference(argName, ref) method

After executing the this.beanFactory.getBean(resolvedName) method, we get the beanB instance with the properties filled and return the instance.

Go back to the applyPropertyValues(beanName, MBD, BW, PVS) method

Performed valueResolver. ResolveValueIfNecessary (pv, originalValue) method, after will get properties filled beanB instance.

Add the B attribute to the deepCopy collection.

Execute bw.setPropertyValues(New MutablePropertyValues(deepCopy)) to fill the B property of beanA.

Go to the setPropertyValues(PVS, false, false) method

The setPropertyValues(PVS, false, false) method is called in the bw.setPropertyValues(New MutablePropertyValues(deepCopy)) method, In this method, each property of the bean is populated (the property is assigned by the setPropertyValues(PVS, false, false) method).

Go back to the applyPropertyValues(beanName, MBD, BW, PVS) method

Bw. setPropertyValues(new MutablePropertyValues(deepCopy)) assigns the elements in deepCopy to each property of beanA. If the b attribute in beanA has been assigned to beanA, and the A attribute in beanB has been assigned to beanA, the infinite nesting mode can be enabled.

Go back to the doCreateBean(beanName, mbdToUse, args) method

After executing the populateBean(beanName, MBD, instanceWrapper) method, unlimited nesting mode can be turned on.

Does the getSingleton(beanName, false) method get beanA this time?

Enter the getSingleton(beanName, false) method

We moved beanA from singletonFactories (level 3) to earlySingletonObjects (level 2) when we injected the A property into beanB. Therefore, beanA can be retrieved from the secondary cache earlySingletonObjects.

Go back to the doCreateBean(beanName, mbdToUse, args) method.

The fetched beanA is eventually returned.

Go back to the createBean(beanName, MBD, args) method.

Execute doCreateBean(beanName, mbdToUse, args) to get the beanA instance and return it.

Return to getSingleton(beanName, () -> {… }.

After execution singletonFactory. GetObject () method will get beanA instance, singletonFactory here is our incoming Lambda expressions (specially used to create the bean instance).

Execute the addSingleton(beanName, singletonObject) method to add beanA to the level-1 cache singletonObjects.

Enter the addSingleton(beanName, singletonObject) method

① Place beanA in the level-1 cache singletonObjects

Delete “beanA” from “singletonFactories”.

③ Remove beanA from the earlySingletonObjects cache (beanA is in the cache)

④ Register beanA’s beanName with registeredSingletons.

Return to getSingleton(beanName, () -> {… } method

Return beanA after adding it to the level-1 cache singletonObjects.

Return to the doGetBean(Name, NULL, NULL, false) method

Execute getSingleton(beanName, () -> {… } method returns the beanA instance.

Return to the preInstantiateSingletons() method

It’s finally over… After executing the getBean(beanName) method, you get the infinite beanA and beanB instances of the doll versions.

Debug Procedure Summary

The loop depends on the specific steps of Debug

Call the doGetBean() method, want to get the beanA, call getSingleton() method to look up the beanA from the cache. In getSingleton() method, look up the beanA from the level 1 cache. No, return NULL.

The beanA in the doGetBean() method is null, so the corresponding processing logic is used to call getSingleton() ‘s overloaded method with an ObjectFactory argument.

In the getSingleton() method, beanA_name is first added to a collection to indicate that the bean is being created. The creatBean() method of the anonymous inner class is then called back.

Enter AbstractAutowireCapableBeanFactory# doCreateBean (), the first reflection calls the constructor to create beanA instance, and then judge. Whether it is a singleton, whether references are allowed to be exposed in advance (generally true for singleton cases), whether it is being created < that is, whether it is in the collection of step 4). At this time, it is detected that beanA is dependent on beanB, so it starts to search for beanB. Call doGetBean(), the same process as beanA above, search for beanB in the cache, if there is no beanB, create, Then fill in the properties for beanB.

At this point, beanB depends on beanA, so call getSingleton () to get beanA, and then search from level 1, Level 2 and level 3 caches successively. At this point, beanA’s creation factory is obtained from level 3 caches, and singletonObject is obtained by creating the factory. This singletonObject points to the beanA instantiated above in the doCreateBean() method.

BeanB then gets beanA’s dependencies, and beanB completes the instantiation and moves beanA from level 3 to level 2.

BeanA then proceeds with the property population, gets the beanB, completes the beanA creation, goes back to the getSingleton () method, and moves beanA from level 2 to level 1 cache