The article directories

  • One, foreword
  • What are cyclic dependencies?
  • Loop dependencies for Spring beans
    • 3.1 Generation of Spring Bean loop dependencies (singleton mode under the loop dependency, do not remember, remember 3.2 three kinds of loop dependency construction)
    • 3.2 Spring’s handling of three cyclic dependencies
      • 3.2.1 Cyclic dependencies: constructors
      • 3.2.2 Cyclic Dependency: Property injection in singleton mode (Goldfinger: default injection only)
      • 3.2.3 Cyclic dependency: Property injection in prototype mode
  • Analysis of Spring’s solution to cycle dependency
    • 4.1 Spring process for creating beans
    • 4.2 “Level 3 Caching” of the Spring Container
    • 4.3 Source Code Parsing
    • 4.4 Process Summary
      • 4.4.1 The entry point is to instantiate and initialize the singleton Bean A. AbstractBeanFactory.doGetBean(“a”)
      • 4.4.2 below into the most complicated AbstractAutowireCapableBeanFactory. CreateBean/doCreateBean () link, create an instance of A
  • 5. Interview Goldfinger
      • Goldfinger: What is circular dependency? What are the three cyclic dependencies in Spring?
      • Goldfinger 2: How does Spring’s infrastructure handle loop dependencies using level 3 caching?
      • Goldfinger 3: Additional question: Why not constructors? Why is singleton property injection ok? Why can’t stereotype mode property injection work?
  • Six, the summary

One, foreword

What are cyclic dependencies?

Cyclic dependencies: N class cyclic (nested) references.

Generally speaking, N beans refer to each other and eventually form a closed loop. A classic illustration would look like this (A, B, and C represent objects, with dotted lines representing references) :

(2) The circular reference here is not a circular call between methods, but the interdependence of objects. (Circular calls between methods can also work if there is an exit)

Loop dependencies for Spring beans

3.1 Generation of Spring Bean loop dependencies (singleton mode under the loop dependency, do not remember, remember 3.2 three kinds of loop dependency construction)

Spring Bean loop dependencies are a new concept to Spring Bean development. You may have the illusion that it’s because you’re working in the cradle of Spring that you’re “safe and sound.

I firmly believe that you must have written code with the following structure in your daily business development:

@Service public class AServiceImpl implements AService { @Autowired private BService bService; . } @Service public class BServiceImpl implements BService { @Autowired private AService aService; . }Copy the code

This is a typical cycle dependency scenario in a Spring environment. But it’s clear that Spring has done a great job of getting around this loop-dependent scenario (Goldfinger: This is one of Spring’s three loc dependencies, singleton property injection, and Spring uses addSingletonFactory() in singletonFactories, the third-level cache, to handle loc dependencies. Therefore, even if we do this circular reference, we can also complete our coding journey ~

3.2 Spring’s handling of three cyclic dependencies

In the Spring environment, because our Bean’s instantiation and initialization are handed over to the container, its cyclic dependencies are represented in three main scenarios. For demonstration purposes, I have prepared the following two classes:

The Spring container has three ways of injecting properties (values or references) : Constructor Each way of injecting a reference creates a circular dependency. How does Spring handle the different injection methods

3.2.1 Cyclic dependencies: constructors

@Service
 
public class A {
 
public A(B b) {
 
}
 
}
 
@Service
 
public class B {
 
public B(A a) {
 
}
 
}
Copy the code

Results: failed to launch an exception is thrown BeanCurrentlyInCreationException

Circular dependencies in the composition of the constructor injection, is unable to solve the circular dependencies mode, and can only be thrown BeanCurrentlyInCreationException abnormal said circular dependencies. This is also the biggest disadvantage of constructor injection

Root cause: Spring’s solution to loop dependencies relies on the concept of a Bean’s “intermediate state,” which is an instantiated, but not initialized, state. The constructor is instantiated, so the constructor’s cyclic dependency cannot be resolved

3.2.2 Cyclic Dependency: Property injection in singleton mode (Goldfinger: default injection only)

This is one of the most common dependency injection methods we use (so you can guess it’s not a problem) :

@Service
 
public class A {
 
@Autowired
 
private B b;
 
}
 
 
 
@Service
 
public class B {
 
@Autowired
 
private A a;
 
}
Copy the code

Results: The project was successfully started and could work normally

Note: Setter method injection is not demonstrated here because the principle is similar to field injection

Goldfinger: Property injection is the default in singleton mode, and this is how junior programmers write code. This type of injection can be handled by Spring, so junior programmers have never encountered loop dependency handling, which shows the elegance of Spring’s packaging design

3.2.3 Cyclic dependency: Property injection in prototype mode

Prototype is rarely used, but it is not unheard of, so attention should be paid to this approach.

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
 
@Service
 
public class A {
 
@Autowired
 
private B b;
 
}
 
 
 
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
 
@Service
 
public class B {
 
@Autowired
 
private A a;
 
}
Copy the code

For property dependencies, the singleton pattern is used by default. There is no need to use the @Scope annotation (for junior developers) on a class to be set up in the SpringIOC container. Can also use the @ Scope (ConfigurableBeanFactory SCOPE_PROTOTYPE) designated as the prototype model

Result: Note that in this case there is no error at startup (because non-singleton beans are not initialized by default, but are initialized when used), so it’s easy to just manually getBean() or @autoWired it within a singleton Bean

// Inject @autoWired private A A;Copy the code

This startup error:

How to solve??

You might have seen the @lazy annotation on the Internet:

@Lazy
 
@Autowired
 
private A a;
Copy the code

I’m responsible for telling you that this doesn’t solve the problem (and probably covers it up). @lazy just delays initialization and will still raise the exception when you actually use it.

Goldfinger: The case for Spring loop dependencies is summarized as follows:

Unresolvable cases: Constructor injects loop dependencies, prototype pattern field injects loop dependencies

Case resolved: Singleton pattern field property injection (setter method injection) loop dependency

Analysis of Spring’s solution to cycle dependency

Before you do that, you need to understand the difference between so-called reference passing and value passing in Java.

Explanation: See this sentence may have small partners want to spray me. Java is clearly all transfer ah, this is my first Java back 100 times of interview questions, how can there be wrong?? Answer: That’s why I have to make this statement: dude, you’re right, Java only has value passing. However, this article uses reference to assist the explanation, I hope you can understand what I want to express ~

Spring’s theory of cyclic dependency is based on Java reference passing, where properties can be set later when a reference to an object is obtained. (But the constructor must be before the reference, after all, your reference is generated for you by the constructor. Can a son be born before his father?)

4.1 Spring process for creating beans

The first thing you need to know is Spring’s process of creating beans. I have drawn its general call stack as follows:

The core three methods of Bean creation are explained as follows:

CreateBeanInstance: instantiates an object by calling its constructor

PopulateBean: Populates the properties. This step is mainly to inject the bean’s dependent properties (@autowired)

InitializeBean: Back to some methods like initMethod, InitializingBean, etc

As you can see from the initialization of the singleton Bean, the loop dependency occurs mainly in the second step (populateBean), which is the processing of the field property injection.

Auric goldfinger:From getBean() doGetBean() createBean() doCreateBean()













AbstractBeanFactory class getBean() and doGetBean() methods

AbstractAutowireCapableBeanFactory createBean in class () method and doCreateBean () method, CreateBeanInstance (), populateBean(), initializeBean()

4.2 “Level 3 Caching” of the Spring Container

Throughout the declaration cycle of the Spring container, the singleton Bean has one and only one object. It’s tempting to think of caching as a way to speed up access. From the source code can also be seen that Spring uses a lot of Cache means, in the process of solving the problem of loop dependency even at the use of “three level Cache”, which is also the subtlety of its design ~

Level 3 caching is more of a Spring container factory term. It uses the level 3 caching pattern to solve the problem of loop dependencies.

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { ... Private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); Private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // Private final Map<String, ObjectFactory<? >> singletonFactories = new HashMap<>(16); // Level 3 cache... /** Names of beans that are currentlyin*/ / This cache is also very important: // It places values at the beginning of bean creation, Has been created to move it out ~ when private final Set < String > singletonsCurrentlyInCreation = Collections. NewSetFromMap (new ConcurrentHashMap<>(16)); /** Names of beans that have already been created at least once. */ / Names of beans that have already been created at least oncesetCollection will not repeat / / is created at least one will put in here ~ ~ ~ ~ private final Set < String > alreadyCreated = Collections. NewSetFromMap (new ConcurrentHashMap<>(256)); }Copy the code

Level 1 caches, level 2 caches, and level 3 caches are all hashmaps, and the key is String but level 1 caches and level 2 caches value is Object, which is used to store singleton objects, and level 3 caches value is ObjectFactory, which is used to store singleton factory objects

Note: AbstractBeanFactory inherited from DefaultSingletonBeanRegistry

(1) singletonObjects: used to store fully initialized beans. Beans removed from this cache can be used directly;

(2) earlySingletonObjects: a cache of pre-exposed singletons holding the original bean objects (not yet populated with properties) to resolve loop dependencies;

(3) singletonFactories: a cache of singletonFactories that store bean factory objects and use them to resolve loop dependencies.

The source code for the singleton Bean is as follows:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
 
...
 
@Override
 
@Nullable
 
public Object getSingleton(String beanName) {
 
return getSingleton(beanName, true);
 
}
 
@Nullable
 
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
 
Object singletonObject = this.singletonObjects.get(beanName);
 
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
 
synchronized (this.singletonObjects) {
 
singletonObject = this.earlySingletonObjects.get(beanName);
 
if(singletonObject == null && allowEarlyReference) { ObjectFactory<? > singletonFactory = this.singletonFactories.get(beanName);if(singletonFactory ! = null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); }}}}returnsingletonObject; }... public boolean isSingletonCurrentlyInCreation(String beanName) {return this.singletonsCurrentlyInCreation.contains(beanName);
 
}
 
protected boolean isActuallyInCreation(String beanName) {
 
returnisSingletonCurrentlyInCreation(beanName); }... }Copy the code
  1. Get it from the first level cache, singletonObjects. (Return if you get it)
  2. If you do not get or object is being created (isSingletonCurrentlyInCreation ()), then again from the second level cache earlySingletonObjects. (Return if you get it)
  3. If not, allow singletonFactories (allowEarlyReference=true) with getObject(). From level 3 cache singletonFactory. GetObject (). If you get it, remove it from singletonFactories and put it in earlySingletonObjects. This is essentially moving from level 3 cache to level 2 cache.

Goldfinger: Why can’t constructor-based loop dependencies be resolved?

SingletonFactories are used for property injection, but the constructor is executed before adding singletonFactories to the level-3 cache, so the constructor’s loop dependency cannot be resolved

The key to Spring’s solution to loop dependencies is the singletonFactories three-level cache. This Cache is filled with ObjectFactories, which are the key to solving the problem.

// It can encapsulate the steps of creating an object into ObjectFactory and give it to the custom Scope to choose whether to create an object to flexibly implement Scope. See Scope interface @functionalInterface Public interface ObjectFactory<T> {T getObject() throws BeansException; }Copy the code

After passing objectFactory.getobject (), it is now placed in the secondary cache earlySingletonObjects. At this point the object is instantiated, not perfectly, but references to the object can be referenced by other references.

Goldfinger: Adding and removing earlySingletonObjects to level 2 cache?

Only add: There is only one place to add data to it, and that is from the level 3 cache in getSingleton()

AddSingleton, addSingletonFactory, and removeSingleton are both mutually exclusive and can be deleted when singleton and singleton factory ObjectFactory are added.

4.3 Source Code Parsing

The Spring container places each Bean identifier that is being created in a pool of currently created beans, where the Bean identifier remains during creation and is purged from the pool of currently created beans.

The “current to create large pools of Bean” refers to the above mentioned singletonsCurrentlyInCreation the collection.

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
 
...
 
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
 
...
 
// Eagerly check singleton cache forManually registered singletons. // If not NULL, the cache will be used ~~ Object sharedInstance = getSingleton(beanName); . // Instead of just checking the type, mark that the Bean has been created ~~ added to the cache, which is called the pool of currently created beansif (!typeCheckOnly) {
 
markBeanAsCreated(beanName);
 
}
 
...
 
// Create bean instance.
 
if(MBD. IsSingleton ()) {/ / the getSingleton method not SingletonBeanRegistry interface methods A public belongs to the implementation class DefaultSingletonBeanRegistry overloading method ~ ~ ~ / / it's characteristic is the execution singletonFactory. GetObject (); BeforeSingletonCreation (beanName) is executed before and after; And afterSingletonCreation (beanName); // Ensure that the Bean is created ~~~~ sharedInstance = getSingleton(beanName, () -> {try {returncreateBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }}... }Copy the code
AbstractBeanFactory public Abstract class AbstractBeanFactory public Abstract class AbstractBeanFactory public Abstract class AbstractBeanFactory AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { ... protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { ... InstanceWrapper = createBeanInstance(beanName, MBD, args); // Create a Bean object and wrap it in a BeanWrapper. // Insert the Bean object from the Wrapper (non-proxy ~~~) and then the Bean will have an address value and can be referenced. Here is the original Object, this is very important to the final Object bean. = instanceWrapper getWrappedInstance (); . EarlySingletonExposure is used to indicate whether a reference to the original object is "pre-exposed" and is used to resolve loop dependencies. // For singleton beans, this variable is normallytrueBut you can also use the attribute allowCircularReferences =falseTo close the circular reference / / isSingletonCurrentlyInCreation (beanName) said that the current bean must in create a Boolean earlySingletonExposure = (MBD) isSingleton ()  && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {
 
if (logger.isTraceEnabled()) {
 
logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } // The getEarlyBeanReference reference will be deleted if the getEarlyBeanReference reference is deleted: This method calls SmartInstantiationAwareBeanPostProcessor. GetEarlyBeanReference () Otherwise don't do anything / /, that is, to the caller a chance. You can create a proxy object by using an AOP method called AbstractAutoProxyCreator, which is AbstractAutoProxyCreator If AOP logic is not required, return Bean addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, MBD, Bean)); } Object exposedObject = bean; //exposedObject is the final object returned... // The fill belongs to the @autoWired dependency ~ populateBean(beanName, MBD, instanceWrapper); // Execute the initialization callback methods ~~~ exposedObject = initializeBean(beanName, exposedObject, MBD); // earlySingletonExposure: If your bean is allowed to be exposed early, that is, referenced in a loop then this is checked // This code is very important ~~~~~ but most people ignore itif(earlySingletonExposure) {// There is no data in the level1 cache, but there is no data in the level2 cache at this timefalse~~~ // This is very clever: : : Because all of the above various instantiation and initialization postprocessors are executed, If you perform on the a / / ((ConfigurableListableBeanFactory) enclosing the beanFactory). RegisterSingleton (beanName, beans); // So the earlySingletonReference you get here will eventually be the Bean you put in manually and return, perfect implementationFlip the switch// We know that executing addSingleton() after executing this doCreateBean is simply adding itself again ** Again, Object earlySingletonReference = getSingleton(beanName,false);
 
if(earlySingletonReference ! InitializeBean () = null) {// This means that if the exposedObject is not changed after initializeBean(), it can be returned without any risk. // initializeBean calls the post-processor, which can generate a proxy object. Then it won't be equal at this point. Let's goelseTo judge!if(exposedObject == bean) { exposedObject = earlySingletonReference; } / / allowRawInjectionDespiteWrapping this value by defaultfalse// hasDependentBean: if the bean has a dependentbean, the bean must be checked.else if(! Enclosing allowRawInjectionDespiteWrapping && hasDependentBean (beanName)) {/ / get it relies on the beans ~ ~ ~ ~ will traverse the below one by one to see ~ ~ String [] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); / / check it one by one so Bean / / removeSingletonIfCreatedForTypeCheckOnly this see below In AbstractBeanFactory / / in short, If it determines that the dependentBean is not already in creation, it removes it from all caches ~~~ and returnstrueOtherwise (for example, it is actually being created) returnfalseInto ourif< span style = "max-width: 100%; clear: both; min-height: 1em;for (String dependentBean : dependentBeans) {
 
if(! removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); }} / / if there is really rely on, and that is an error (don't wait for memory to remove you to an error, it is very unfriendly) / / this exception is BeanCurrentlyInCreationException, error log also watch out for a little, convenient positioning error ~ ~ ~ ~if(! actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +
 
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
 
"] in its raw version as part of a circular reference, but has eventually been " +
 
"wrapped. This means that said other beans do not use the final version of the " +
 
"bean. This is often the result of over-eager type matching - consider using " +
 
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); }}}}return exposedObject;
 
}
Copy the code
// The only place this method is called is at the last check of the loop dependency ~~~~~ protected Boolean RemoveSingletonIfCreatedForTypeCheckOnly (String beanName) {/ / if the bean is not created Such as ForTypeCheckOnly then removeif(! this.alreadyCreated.contains(beanName)) { removeSingleton(beanName);return true;
 
}
 
else {
 
return false; }}}Copy the code

For example, in the case of field property dependency injection, populateBean will first complete the instantiation and initialization of the bean it depends on to inject, and finally return to this process to continue processing, so there is no problem with Spring handling this way.

Here’s a little detail:

if (exposedObject == bean) {
 
exposedObject = earlySingletonReference;
 
}
Copy the code

If exposedObject == bean means that the object returned is the original object, it has not been represented by populateBean and initializeBean. ExposedObject = earlySingletonReference = exposedObject = earlySingletonReference

4.4 Process Summary

Taking the above interdependent injection of class A and B as an example, the trend of key codes is expressed here:

4.4.1 The entry point is to instantiate and initialize the singleton Bean A. AbstractBeanFactory. DoGetBean (” a “)

protected <T> T doGetBean(...) {... / / tag beanName a is has been created at least one ~ ~ ~ it would have been saved in the cache will not be removed (unless the thrown exception) / / see the cache Set < String > alreadyCreated = Collections. NewSetFromMap (new  ConcurrentHashMap<>(256))if(! typeCheckOnly) { markBeanAsCreated(beanName); } // a does not exist in any level 1 cache, and is not being created. Object beanInstance = getSingleton(beanName, beanName)false); . // This getSingleton method is critical. / / 1, mark is to create a ~ / / 2, call singletonObject = singletonFactory. GetObject (); The createBean() method is called, so this step is the most critical //3, the instance has been created, the a will be removed from the entire created cache //4, the addSingleton() to add. SharedInstance = getSingleton(beanName, () -> {... sharedInstance = getSingleton(beanName, () -> {...return createBean(beanName, mbd, args); });
 
}
Copy the code

4.4.2 below into the most complicated AbstractAutowireCapableBeanFactory. CreateBean/doCreateBean () link, create an instance of A

protected Object doCreateBean() {... InstanceWrapper = createBeanInstance(beanName, MBD, args); // The bean is"The original beans"Is here A instance objects: A @ 1234 final Object bean. = instanceWrapper getWrappedInstance (); . / / whether to exposure (allows circular dependencies) now here in advance is allowed A Boolean earlySingletonExposure = (MBD) isSingleton () && enclosing allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); // Bind A to the ObjectFactory and register it in the "singletonFactories" with the getEarlyBeanReference method This is where the automatic proxy creator creates the proxy object (note that the execution time is when tertiary caching is performed)if(earlySingletonExposure) { addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }... // exposedObject is the final Object returned, in this case the original Object bean (A@1234), with the following useful Object exposedObject = bean; // To complete the assignment of the A@1234 attribute, @autoWired is in effect here ~ // hence the call to getBean("b"// Here we assume that B is already created as B@5678 // Note that in populateBean("b"), so call getBean("a") will eventually call getSingleton("a"// The getEarlyBeanReference method will be executed. This also explains the root reason why we @AutoWired are proxy objects rather than ordinary objects populateBean(beanName, MBD, instanceWrapper); // instantiate. Two methods of the post-processor BeanPostProcessor are executed here. PostProcessAfterInitialization () it is possible to return a proxy object, Such exposedObject object is no longer the original special attention oh ~ ~ ~ / / such as processing @ Aysnc AsyncAnnotationBeanPostProcessor it is in this time to generate a proxy object (a pit, Be careful with @aysnc) exposedObject = initializeBean(beanName, exposedObject, MBD); . // check if there is a cyclic reference problem ~~~~~if(earlySingletonExposure) {// Note the second parameter passed herefalse, said not to triple in the cache singletonFactories again to call getObject () method of the ~ ~ ~ / / above to speak to because at the time of initialization, B Objectfactory.getobject () triggers objectFactory.getobject () so A is already in the secondary cache earlySingletonObjects. A@1234 Object earlySingletonReference = getSingleton(beanName,false);
 
if(earlySingletonReference ! = null) {// This equation says that exposedObject is equal if it has not been proxyed again // Obviously the exposedObject of our a object here is not proxyed soifWill go in ~ // this situation, all over ~~~if(exposedObject == bean) { exposedObject = earlySingletonReference; } // If the method has the @aysnc annotation, then the exposedObject is A proxy object, so it has A hasDependentBean(beanName)true< < getDependentBeans(beanName) >"b"] this dependencyelse if(! this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); // A@1234 depends on ["b"If there is an actual dependentBean: actualDependentBeans not empty then throw an exception proof loop reference ~for(String dependentBean: dependentBeans) {/ / the judgment principle is: if this time and has not been created good b, enclosing alreadyCreated. The contains (beanName) =trueIndicates that the bean has already been createdfalse// If the bean is not in the alreadyCreated cache, it has not been created.if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
 
actualDependentBeans.add(dependentBean);
 
}
 
}
 
if(! actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +
 
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
 
"] in its raw version as part of a circular reference, but has eventually been " +
 
"wrapped. This means that said other beans do not use the final version of the " +
 
"bean. This is often the result of over-eager type matching - consider using " +
 
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
 
}
 
}
 
}
 
}
 
}
Copy the code

Since the steps of key code parts are not easy to be broken down, in order to express them more concretely, the following diagrams are used to help friends understand:

5. Interview Goldfinger

Goldfinger: What is circular dependency? What are the three cyclic dependencies in Spring?

The answer is part 2 and 3.2, just memorize it

Goldfinger 2: How does Spring’s infrastructure handle loop dependencies using level 3 caching?

The interview won’t make you read the source code line by line, but explain the whole idea

First, the code, singleton mode property injection creates cyclic dependencies

@Service public class AServiceImpl implements AService { @Autowired private BService bService; . } @Service public class BServiceImpl implements BService { @Autowired private AService aService; . }Copy the code

Second, the execution diagram

Third, explain the process

To create a Bean in Spring, Spring first tries to fetch it from the cache, which is called singletonObjects. The main method is:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
         singletonObject = this.earlySingletonObjects.get(beanName);
         if(singletonObject == null && allowEarlyReference) { ObjectFactory<? > singletonFactory = this.singletonFactories.get(beanName);if(singletonFactory ! = null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); }}}}return(singletonObject ! = NULL_OBJECT ? singletonObject : null); }Copy the code

First, explain two parameters:

IsSingletonCurrentlyInCreation (1) whether the corresponding singleton in the creation, when the singleton has not been initialized completely (for example, A constructor defined dependent on the B object, have to go to create A B object, or in the process of populatebean depend on the object B, Object B is created while object A is being created.

AllowEarlyReference allows getObject to fetch objects from singletonFactories.

Analyzing the entire process of getSingleton, Spring first tries to fetch it from singletonObjects (level 1 cache), or if it cannot and the object is being created, from earlySingletonObjects(level 2 cache). If still can’t get and allow from singletonFactories, captured by the getObject by singletonFactory. GetObject () (3). If you get it

this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
Copy the code

Remove the corresponding singletonFactory and add the singletonObject to earlySingletonObjects, which is actually upgrading the level 3 cache to level 2 cache!

Spring uses the singletonFactories cache (ObjectFactory) to solve this problem:

public interface ObjectFactory<T> {
    T getObject() throws BeansException;
 }
Copy the code

In bean creation, there are two important anonymous inner classes that implement this interface. A place is

new ObjectFactory<Object>() {
   @Override   public Object getObject() throws BeansException {
      try {
         returncreateBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; }}Copy the code

As mentioned above, Spring uses it to create beans (which is really unclear…).

The other is:

addSingletonFactory(beanName, new ObjectFactory<Object>() {
   @Override   public Object getObject() throws BeansException {
      return getEarlyBeanReference(beanName, mbd, bean);
   }});
Copy the code

This is where the loop dependency is resolved. This code occurs after createBeanInstance, which means that the singleton has already been created. The object has already been produced, and while it’s not perfect (not yet initialized in steps 2 and 3), it’s already recognizable (it can be located to objects in the heap based on object references), so Spring now exposes the object for everyone to recognize and use.

What’s the good of that? Let’s look at the circular dependency case where A field or setter of A depends on an instance object of B and A field or setter of B depends on an instance object of A. After A completes the first initialization and exposes itself to singletonFactories, A tries to get(B) and finds that B has not yet been created. B finds that it depends on object A when it initializes the first step, so it tries get(A), singletonObjects(definitely not, because A is not fully initialized), earlySingletonObjects (also not), Try singletonFactories at level 3. Because ObjectFactory exposes itself, ObjectFactory. GetObject gets ObjectFactory (although A is not fully initialized, it’s better than nothing). B successfully completed the initialization stages 1, 2 and 3 after getting the object A, and put itself into the level-1 cache singletonObjects after complete initialization. At this time, the object A can get B successfully completed its initialization phase 2 and 3. Finally, A also completed initialization, grew up, and entered the level 1 cache singletonObjects. Fortunately, because B got the object reference of A, the object B now holds also became perfect. Everything is so magical!!

Goldfinger 3: Additional question: Why not constructors? Why is singleton property injection ok? Why can’t stereotype mode property injection work?

First, why not constructors? (Contrast with singleton schema property injection)

The Spring container places each Bean identifier being created in a currently created Bean pool, where the Bean identifier remains during creation.

So if in the process of creating Bean has found itself in the “current to create large pools of Bean” will be thrown when abnormal BeanCurrentlyInCreationException said circular dependencies; Beans that have been created are cleared from the currently created Bean pool.

According to the above source code implementation analysis: The Spring container creates the singleton StudentA, which depends on StudentB, and places A in the “currently created Bean pool”. At this time to create StudentB, StudentC StudentB dependence, and then will be placed in the “current to create large pools of Bean” B, so creating StudentC StudentC rely on StudentA again, but, as the StudentA in the pool, so complains, Since the beans in the pool are not initialized, they rely on an error.

** Root cause – unable to construct an instantiated intermediate state that is not initialized: Property injection in singleton mode is done using singletonFactories that solve circular dependencies relying on the concept of the Bean’s “intermediate state”, which refers to the state that has been instantiated but not initialized. The intermediate state in the singleton pattern is obtained by calling the no-parameter constructor. The parameterized constructor is initialized directly. Using the parameterized constructor directly cannot construct the instantiated and uninitialized state, so the constructor’s cyclic dependency cannot be resolved. 支那

Second, why does singleton property injection work?

The first two steps in the figure show that Spring instantiates the Bean object before setting the object properties



Spring first instantiates the Bean object with a construct (No parameter constructor), Spring puts the instantiated object into a Map, and Spring provides a way to get a reference to the instantiated object with an unset property.

Third, why doesn’t stereotype mode property injection work? (Compared to the singleton pattern)

In prototype mode, scope= “prototype” means that an instance object is created for each request.

The difference between the Prototype and singleton patterns is that stateful beans use the Prototype scope, while stateless beans generally use the Singleton singleton scope.

For “prototype” scoped beans, the Spring container can’t do dependency injection because it doesn’t cache prototype-scoped beans (Spring’s tertiary cache doesn’t hold Bean objects that aren’t singletons). Therefore, there is no way to expose a Bean being created in advance.

When scope=”prototype”, Spring bean’s initial loop dependency can only handle the singleton’s set mode. Spring’s level 3 cache does not store non-singleton bean objects. Unable to handle loop dependencies.

In a word: Why does the singleton pattern work? Interview goldfinger 2. Third, explain the process. Why not constructors? Unable to construct an instantiated intermediate state that is not initialized: Property injection in singleton mode is done using singletonFactories that solve circular dependencies relying on the concept of the Bean’s “intermediate state”, which refers to the state that has been instantiated but not initialized. The intermediate state in the singleton pattern is obtained by calling the no-parameter constructor. The parameterized constructor is initialized directly. Using the parameterized constructor directly cannot construct the instantiated and uninitialized state, so the constructor’s cyclic dependency cannot be resolved. Why can’t stereotype mode property injection work? Spring’s three-level cache does not hold bean objects that are not singletons

Six, the summary

How does Spring use level 3 caching underneath to handle loop dependencies? complete

Play code every day, progress every day!!