Today’s sharing started, please give us more advice ~
How does Spring handle loop dependencies? This is one of the most frequently asked interview questions these days. This article goes into more detail about how loop dependencies are handled in Spring.
What are circular dependencies
A dependency refers to a bean-to-bean dependency, and a circular dependency refers to a dependency between two or more beans, such as:
Constructor loop dependencies
Code examples:
The configuration file
Setter loop dependency
Code sample
The configuration file
Loop dependencies include constructor injection loop dependencies set, injection loop dependencies, and prototype pattern Bean loop dependencies. Spring only addresses setter injection loop dependencies for simple profit beans. Constructor loop dependencies and prototype mode loop dependencies are not addressed. “BeanCurrentlyInCreationException”.
Circular dependencies control switch in AbstractRefreshableApplicationContext container factory class definition:
Cyclic dependencies between beans are allowed by default, and Spring tries to handle cyclic dependencies during dependency injection. If this property is configured to “false” it turns off circular dependencies and throws an exception when a circular dependency is encountered during Bean dependency injection. It can be turned off in one of the following ways, but this is not usually done.
The constructor loops for dependent processing
For example, A depends on B, B depends on C, C depends on A. While instantiating A, the constructor needs to inject B, and then Spirng instantiates B, where A is in the “being created” state. When we instantiate B, we find that the constructor needs to inject C and then de-instantiate C, but when we instantiate C, we need to inject an instance of A. This creates an infinite loop where we can never instantiate A Bean first, so Spring throws an exception when it encounters A constructor loop dependency.
So how does Spring do it?
Spring will first go through the Bean’s instantiation process to try to create an instance of A, looking for whether A is being created in the pool of beans being created (A cache Map) before creating the instance. If not, it will place A in the Pool of beans being created, and then prepare the instantiation constructor parameter B.
Spring will go through the Bean’s instantiation process to try to create an instance of B, looking to see if B is being created from “Pool of beans being created” (A cache Map) before creating the instance. If not, it will put B into “Pool of beans being created” and prepare the instantiation constructor parameter A.
Spring will go through the Bean’s instantiation process to try to create an instance of A, looking for whether A is being created from the pool of beans being created (A cache Map) before creating the instance.
At this time: Spring found A is in “is to create large pools of Bean”, said A circular dependencies, the constructor throws an exception: “BeanCurrentlyInCreationException”.
DefaultSingletonBeanRegistry#getSingleton
Below, we take BeanA structural parameters dependent on BeanB and BeanB structural parameters dependent on BeanA as an example for analysis.
When Spring’s IOC container starts, try to initialize the simple Bean BeanA. According to the previous analysis, the simple Bean creation entry is AbstractBeanFactory#doGetBean. In this method, it will fetch the simple Bean cache first.
DefaultSingletonBeanRegistry#getSingleton(jString beanName, ObjectFactory<? > singletonFactory) method, in which the created Bean is first added with a name of
SingletonsCurrentlyInCreation ConcurrentHashMap, which means the Bean is created, and then call the ObjectFactory. GetObject () instantiated Bean, Suppose BeanA enters the method to instantiate:
BeforeSingletonCreation method is critical, it will add beanName singletonsCurrentlyInCreation, a representative in the ConcurrentHashMap “is creating the Bean”.
If not the beanName singletonsCurrentlyInCreation, stored in the Bean singletonsCurrentlyInCreation,
If the Bean of singletonsCurrentlyInCreation, error abnormal BeanCurrentlyInCreationException circular dependencies.
【 note 】 means that the same beanName into 2 times this method will throw exceptions, BeanA has joined singletonsCurrentlyInCreation now.
AbstractAutowireCapableBeanFactory#autowireConstructor
Objectfactory. getObject instantiate Bean we have looked at the details of the objectFactory. getObject instantiate Bean. Because our BeanA injected a BeanB constructor, so the code will eventually go AbstractAutowireCapableBeanFactory# autowireConstructor, instantiation BeanA by constructor.
In autowireConstructor approach through ConstructorResolver# resolveConstructorArguments to parse the structure parameters, Call BeanDefinitionValueResolver to ref = “beanB” string reference into a real Bean, Namely BeanB, so in the parser BeanDefinitionValueResolver attribute value to instantiate the BeanB, will also go to DefaultSingletonBeanRegistry# getSingleton the BeanB add SingletonsCurrentlyInCreation “is to create large pools of Bean”, and then call the ObjectFactory. GetObject instantiation BeanB.
Below the BeanB also need through the constructor to create, BeanB constructor parameters depend on the BeanA, means and invokes the BeanDefinitionValueResolver to ref = “BeanA” this string reference into containers BeanA Bean instance, Then the code will go DefaultSingletonBeanRegistry# getSingleton. And then again try to add BeanA singletonsCurrentlyInCreation “is to create large pools of beans”.
The BeanB constructor parameter is dependent on BeanA, so BeanA wants to enter the bean-in-process pool again. Spring throws a loop dependency exception:
Error creating bean with name ‘beanA’ : Requested bean is currently in creation: Is there an unresolvable circular reference?
At this point, the source code analysis of Spring’s handling of constructor loop dependencies is complete.
Setter loops depend on processing
Setter loop dependencies are allowed. Spring implements loop dependencies by exposing the ObjectFactory of an uninstantiated Bean in advance so that other beans can reference that Bean through the ObjectFactory.
The implementation process is as follows:
Spring creates the BeanA, instantiates it with a no-argument construct, and exposes an ObjectFactory to get the BeanA being created, then adds the BeanA to the pool of beans being created, and then injects the BeanB via setters.
Spring creates the BeanB, instantiates it with a no-argument construct, and exposes an ObjectFactory to get the BeanB being created, then adds the BeanB to the pool of beans being created, and then injects the BeanA through setters.
Since BeanA pre-exposes the ObjectFactory when BeanB injects BeanA through setters, returning it pre-exposes a BeanA being created.
Then complete BeanB dependency injection.
AbstractAutowireCapableBeanFactory#doCreateBean
BeanA relies on BeanB via settter, and BeanB relies on BeanA via setter. As we learned in the previous Bean instantiation process, Bean instantiation will go AbstractBeanFactory# doGetBean, then look for simple interest cache the Bean, if there is no call DefaultSingletonBeanRegistry# getSingleton, Methods will add BeanA singletonsCurrentlyInCreation Bean pool in “create”, and then call the ObjectFactory. GetObject create Bean.
Org. Springframework. Beans. Factory. Support. AbstractBeanFactory# doGetBean source code:
Come in for the first time, the cache is not BeanA, so will walk getSingleton method, then the code will eventually go AbstractAutowireCapableBeanFactory# doCreateBean method.
AbstractAutowireCapableBeanFactory# doCreateBean source code:
After the BeanA is instantiated, the ObjectFactory is stored in a singletonFactories (HashMap) that exposes the Bean’s creation factory ahead of time. Then call populateBean to go through the property injection process.
Injection will get bean by BeanDefinition dependence attribute, and then call AbstractAutowireCapableBeanFactory# applyPropertyValues, apply attributes to the object. In applyPropertyValues approach eventually call BeanDefinitionValueResolver# resolveValueIfNecessary parsing attribute values, such as: ref = “beanB” this string reference into a reference object instance.
In BeanDefinitionValueResolver parsing dependent attribute values: BeanB, also triggers BeanB instantiation, and the code goes to AbstractBeanFactory#doGetBean, Then go method DefaultSingletonBeanRegistry# getSingleton the BeanB add singletonsCurrentlyInCreation Bean pool in “create”, Code will then go to AbstractAutowireCapableBeanFactory# doCreateBean method created in the BeanB,
In this method, BeanB is instantiated, and BeanB’s ObjectFactory is stored in singletonFactories (HashMap) to pre-expose the Bean’s creation factory, which is used to resolve the loop dependency. Then call populateBean to go through the property injection process.
Also because BeanB injected A through the Setter, the populateBean property injection process resolves ref= “beanA” as an instance of beanA in the container.
It then goes to AbstractBeanFactory#doGetBean to get an instance of BeanA. This is a different process, so let’s take a look at the code in AbstractBeanFactor #doGetBean
In obtaining simple interest when the instance of the Bean is will go to the simple interest Bean cache to see if the Bean already exists, if not, then will go DefaultSingletonBeanRegistry# getSingleton method creates a Bean.
The problem is: There is already BeanA in the single-benefit Bean cache at this point, because BeanA was already in the “creating Bean pool” at the beginning. Let’s first look at how to get the Bean from the cache.
DefaultSingletonBeanRegistry# getSingleton (Java. Lang. String, Boolean) source is as follows:
Here is the classic three-level cache solution to Spring loop dependencies. As you can see, the Bean is first fetched from the singletonObjects single-benefit Bean cache (which is the Bean that has been instantiated). If not, The beans are fetched from earlySingletonObjects’ early object cache (which holds early beans that have not yet been instantiated) or, if not, the exposed ObjectFactory from singletonFactories to retrieve the dependent beans. And then put it in the early cache. And remove the ObjectFactory from singletonFactories. Finally, an instance of the Bean is returned.
Because at the time of instantiation BeanA have put BeanA ObjectFactory added to singletonFactories cache, then here will go to singletonFactory. The getObject (); Method gets an instance of BeanA and stores the BeanA in the earlySingletonObjects early single-profit Bean cache.
If the instance of BeanA returns successfully, the setter injection of BeanB is successful, which means that BeanB is instantiated. If the setter method of BeanA is injected successfully, BeanA is instantiated.
Prototype pattern loop dependencies
Prototype beans do not allow cyclic dependencies because they are not cached, so ObjectFactory cannot be exposed and cyclic dependencies cannot be implemented.
conclusion
Do not know if you have to see dizzy, anyway I but in the source process is quite hard ~~~~(>_<)~~~~, here you need to be familiar with the front Bean instantiation process and attribute injection process, otherwise it will be dizzy.
Here’s a summary:
Constructor circular dependencies are not allowed, mainly through singletonsCurrentlyInCreation “is to create large pools of Bean” to create the Bean cache, if the circular dependencies, the same Bean will try to enter the cache 2 times, throwing circular dependencies.
Setter loop dependencies are allowed. Spring implements loop dependencies by exposing the ObjectFactory of an uninstantiated Bean in advance so that other beans can reference that Bean through the ObjectFactory. Level 3 caching is used when fetching dependent beans.
Today’s share has ended, please forgive and give advice!