First, the cause of circular dependence

Before exploring Spring’s three-level caching solution to circular references, we need to understand what Spring’s so-called circular dependencies are, how they occur, and why they occur.

image-20200923091530338

This is a classic circular reference problem, where the instantiation of one class depends on the instantiation of another class. If we create the beans ourselves instead of using Spring to manage the two beans, the implementation of this circular reference is extremely simple:

image-20200923092015113

Why is It so difficult for Spring to resolve circular dependencies? Because Spring creates a Bean that needs to be built by reflection, the build process has no sense of what type the class is, it can only instantiate one to populate one entity! So:

  • createUserServiceImplDependencies are found after completionEmailServiceImpl
  • Then create aEmailServiceImpl, but after creating it, you find that it depends onUserServiceImpl!
  • So I went and created itUserServiceImpl, and found thatEmailServiceImpl
  • And then I create it againEmailServiceImpl
  • .

The cycle goes on and on, creating the problem of cyclic dependency! Thus we cycle continuously create, thus caused the death of continuous cycle, the Spring will throw BeanCurrentlyInCreationException exception!

How to solve this problem? The paradox of circular dependencies is that UserServiceImpl needs EmailServiceImpl to create UserServiceImpl, and UserServiceImpl needs UserServiceImpl to create EmailServiceImpl, and neither bean can be created!

How to solve circular dependency

We can create two containers (maps), one named singletonObjects and one named earlySingletonObjects!

  • “SingletonObjects” : a pool of singletons where we can store objects that have been created and have their properties injected!
  • “EarlySingletonObjects” : exposed objects that have been created but not injected!

Now that we have these two Map objects, let’s try again to create a bean that is cyclically dependent!

  • createUserServiceImplWhen you’re done, save yourself toearlySingletonObjectsGo inside and find dependenceEmailServiceImpl
  • “SingletonObjects” is not available, and then “earlySingletonObjects” is not available.
  • createEmailServiceImplWhen you’re done, store yourself inearlySingletonObjectsGo inside and find dependenceUserServiceImpl!
  • And he tried to getsingletonObjectsLook for, obviously there is no, and then toearlySingletonObjectsI looked inside, and I found itUserServiceImplObject!
  • willearlySingletonObjectsObject returnedUserServiceImplSet it toEmailServiceImplTo create complete!
  • Place yourself in “singletonObjects” and remove yourself from “EarlysingtonObjects”! Return!
  • UserServiceImplReturns theEmailServiceImplSet to the corresponding property, creation is complete!
  • Place yourself in “singletonObjects” and remove yourself from “EarlysingtonObjects”! Return!

So far we’ve solved the circular reference problem!

At the very least, we now know that we need at least two conditions to solve circular dependencies:

  • Circular dependencies must be resolved through reflection to create the object. If you use construct parameter injection instead of property injection, you will have problems because Spring has no way to instantiate objects, let alone property injection!
  • Beans that rely on loops must be singletons. If they are not singletons, the infinite loop problem we mentioned above will arise!

legend

image-20200923141100566

Why does Spring use level 3 caching?

From the above explanation, we probably understand the solution of circular dependency. Spring also solves this problem, but Spring’s consideration is much more detailed than ours. We clearly can solve circular dependency by using the second-level cache, but why does Spring use the third-level cache?

Let’s first look at the names of each Spring cache and what they do:

  • “SingletonObjects” : a pool of singletons where we can store objects that have been created and have their properties injected!
  • “EarlySingletonObjects” : exposed objects that have been created but not injected!
  • **singletonFactories: ** exposed objects that hold factory objects that have been created but have not yet been injected! This factory returns the object!

Why is that? In fact, the root cause of Spring’s recurring dependencies being mentioned by many people is that why use level 3 caching to solve a problem that can be solved with level 2 caching?

Our above design solution is a good solution to the problems caused by circular dependency, but please consider a question:

What if we create a bean that depends on an object that needs to be propped up by Aop? In this case, we can’t just put the created object in the cache! Why? Because we expect to inject a proxied object, not the original object!

So instead of just putting a raw object in the cache, we can just check it out, proxy it and put it in the cache if we need Aop!

But think about where you did Aop in the last review. This is done in the last step of the Spring declaration cycle! However, if we were to do this, the Aop proxy logic would do the Aop proxy at instance creation, which clearly does not conform to Spring’s definition of the Bean lifecycle!

So Spring redefines a “singletonFactories” to hold a factory object for a Bean. After creating the object, you put the “singletonFactories” in the cache without instantiating them before you fill in the properties. He calls the factory method to return a proxy object only if a circular reference occurs or an object depends on him, thus ensuring Spring’s definition of the Bean lifecycle!

Let’s first look at the definition of level 3 cache!

image-20200923144327428

Instead of holding a Bean object like level-1 and level-2 caches, it holds an ObjectFactory object. What does this object do? We need to go deeper!

image-20200923144716172

This is the ObjectFactory implementation in the Spring level 3 cache! Function is roughly, iterate through all the BeanPostProcessor post processor, if found rear SmartInstantiationAwareBeanPostProcessor type of processor, also is the post processor handling of Aop, it returns an Aop processed object, Return an incoming bean if the class is not proxied!

img

Four, from the source view of circular reference

UserServiceImpl = UserServiceImpl; UserServiceImpl = UserServiceImpl; UserServiceImpl = UserServiceImpl;

image-20200923152629744

The first fetch must be null because no one is putting data into these caches! Once you get the empty object, start creating the object!

image-20200923153403696

Once the object is created, wrap it as a factory object and place it in the level 3 cache!

image-20200923153700614

Then, start filling the properties [” EmailServiceImpl “]!

image-20200923153855960

During the population process, getBean is called to see if there are any objects in the cache that need to be injected

image-20200923154354974

We will find that we are back to the logic of the first step, and we can’t get any objects! So go down, start creating the object, and then place the created object [” EmailServiceImpl “] into the level 3 cache!

Then I started the attribute injection again, and found a dependency on [UserServiceImpl], so I started trying to get [UserServiceImpl] from the bean container again, so I went to the first step again!

UserSercieImpl is stored in the level 3 cache at the time of creation, so it is possible to retrieve the data.

image-20200923154522453

The object is then returned, injected into the corresponding property, and returned to the entire life cycle of the [EmailServiceImpl] Bean. And then all the way back to the call that created the bean! Put [EmailServiceImpl] in the level 1 cache

image-20200923155340502

This object is then returned to the [UserServciceImpl] injection logic, and finally [EmailServiceImpl] ** is injected into the UserServiceImpl, which is then returned to the call to create the Bean and placed in the level 1 cache. Finally, the entire circular reference is complete!

【 Recommended Reading 】

  • Spring: No one knows more about Java object creation than I do!
  • Take you out of the source hell, from the principle of understanding MyBatis for Spring source extension implementation
  • Do you know the Spring BeanFactoryPostProcessors is how to do?
  • Want to learn the Spring source code, you must know will BeanDefinition principle!
  • This is the end of the Spring business question you asked countless people during the interview?
  • The virtual @Configuration Configuration class is seen in Spring
  • Angry! Interviewer: Come here, I’ll hand write a Spring Aop implementation for you!
  • Ten thousand words long, help you deep travel Spring cycle rely on source code!
  • Heard you read the Spring source code confused? I put up the shelf for you, and you fill it out!

If there is an error in the understanding of the article, you are welcome to talk to the bosses. Welcome to follow the author’s public number [JAVA program dog], progress together, learn together!