concept

Multiple beans depend on each other to form a closed loop. For example, A depends on B, B depends on C, and C depends on A. 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.

IO /spring-fram…

As long as A’s injection mode is setter and singleton, there will be no problem with AB cyclic dependency.

Code demo

public class A {

   private B b;

   public B getB(a) {
      return b;
   }

   public void setB(B b) {
      this.b = b;
   }

   public A(a) {
      System.out.println("Constructor of object A executes.............."); }}Copy the code
public class B {

   private A a;

   public A getA(a) {
      return a;
   }

   public void setA(A a) {
      this.a = a;
   }

   public B(a){
      System.out.println("Object B constructor execution........"); }}Copy the code

The applicationContext.xml configuration file is as follows:


      
<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">


   <bean id="a" class="com.luban.service.A" >
      <property name="b" ref="b"/>
   </bean>

   <bean id="b" class="com.luban.service.B">
      <property name="a" ref="a"/>
   </bean>
</beans>
Copy the code

The test classes are as follows:

public class Test {
   public static void main(String[] args) {
      ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");

      A a = ac.getBean("a", A.class);
      B b = ac.getBean("b", B.class); }}Copy the code

Three levels of cache

As l3 cache is refers to the three in the class DefaultSingletonBeanRegistry Map container, as follows:

// This is the micro SpringIOC container.
// Hold Bean objects that have gone through a full life cycle
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name --> ObjectFactory */
//
   // Store the factory that can generate beans
private finalMap<String, ObjectFactory<? >> singletonFactories =new HashMap<>(16);

/** Cache of early singleton objects: bean name --> bean instance */
//
// Store early exposed Bean objects before the end of the Bean life cycle (attributes are not yet filled)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
Copy the code
  • First-level cache < also called singleton pool) singletonObjects: hold Bean objects that have gone through their full life cycle

  • Level 2 cache: earlySingletonObjects, which holds early exposed Bean objects whose life cycle is not over (properties are not fully populated)

  • Level 3 cache: Map<String, ObiectFactory<? >> singletonFactories, which stores factories that can generate beans

The cycle dependency resolution process is as follows:

  • GetSingleton = singletonObjects = singletonObjects = singletonObjects = singletonObjects; The method call isSingletonCurrentlyInCreation method of judging whether the object is created, in what is called creation? That is, the object has been instantiated, but no attribute has been assigned to the object. If it is being created, check to see if the object is present in the level 2 cache, and if not, then check the level 3 cache.

  • 2. After the object is instantiated, the addSingletonFactory method is called to put the object into the Spring level-three singletonFactories before the populateBean is called, and then the attribute is assigned and initialized.

  • 3. After the object is instantiated, assigned and initialized, the addSingleton method is called to place the object into the level-1 cache singletonObjects.

The detailed steps for cyclic dependencies are as follows:

The method getSingleton () is used to check if the object exists in the cache, and the method populateBean () is used to assign properties to the object. After instantiating object B, call populateBean for attribute assignment, and find that it depends on object A, so create object A. Call getSingleton method to query that there is no object in level 1 cache, but object A has been instantiated before, but has not been assigned attribute. That is, it is in the creation process, so it queries the level 2 cache and level 3 cache to see if this object exists, of course it is in level 3 cache, so it gets this object, and moves it from level 3 cache to level 2 cache; After the assignment is complete, the object B is initialized, and then the addSingleton method is called to place the object into the singletonObjects cache. Then it comes back to assign the B property of object A and initialize object A. After initialization, addSingleton method is called to place the object into singletonObjects.

The main methods involved are shown in the figure below

Source code analysis

/** * This method does the IOC container creation and initialization work * the most important steps in this method are the second and eleventh steps ** /
@Override
public void refresh(a) throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      // STEP 1: refresh preprocessing
      // Preparations include setting the startup time, whether to activate the identity bit, and initializing the property source configuration
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      / / use DefaultListableBeanFactory is DefaultListableBeanFactory subclass
      // Initialize the bean factory

      / / STEP 2:
      / / a) to create the IoC container (DefaultListableBeanFactory)
      // b) load the parsed XML file (eventually stored in the Document object)
      // c) read the Document object and complete the loading and registration of BeanDefinition
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      // The most important method, prepare the bean factory
      // STEP 3: Preprocess the IoC container (set some common attributes)
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.
         // This method does not have any code in the current version of Spring, perhaps spring expects it to be extended in a later version
         // Empty shell method
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         // More important method
         // Execute the registered Factory Processors in spring's environment
         / / set custom ProcessorFactory and defined within a spring (ConfigutationClassPoetProcessor)
         / / spring BeanFactoryPostProcessor ConfigurationClassPostProcessor is maintained within a spring
         / / the following method in the main executive ConfigurationClassPostProcessor method
         // STEP 5: Call BeanFactoryPostProcessor to process BeanDefinition

         /** * BeanFactoryPostProcessor is one of spring's extension points. Spring allows BeanFactoryPostProcessor to read its configured metadata before the container instantiates any other beans * and can modify it as needed, For example, you can change the scope of the bean from Singleton to Prototype. You can also change the value of the property to * You can configure multiple spring BeanFactoryPostProcessor at the same time, and by setting the 'order' properties to control each spring BeanFactoryPostProcessor * BeanFactoryPostProcessor is executed after the Bean definition file is loaded by the Spring container and before the bean is instantiated
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         // The previous line of code already put some of the post-processors into bdMap, including the custom BeanPostProcessor
         // Register the BeanPostProcessor
         // Remove all the post-processors in bdMap,
         // Add some other post-processors to the factory list
         // STEP 6: Register the BeanPostProcessor
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         // Not important, internationalization processing
         // STEP 7: Initialize some message sources (such as i18N for internationalization)
         initMessageSource();

         // Initialize event multicaster for this context.
         // It is not important to handle the event
         // STEP 8: Initializes the application event broadcaster
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         // This is an empty shell method with no code in it
         // STEP 9: Initialize some special beans
         onRefresh();

         // Check for listener beans and register them.
         // Wait for some listener registrations
         STEP 10: Register some listeners
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         // key, key
         // Complete the bean instantiation
         // STEP 11: Instantiate the remaining singleton beans (not lazy-loaded)
         // Note: The IoC, DI, and AOP of beans all occur in this step
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         // STEP 12: Publish the corresponding events when the refresh is completefinishRefresh(); }}Copy the code

Look at step 11: finishBeanFactoryInitialization (the beanFactory) method

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
   // leave out unimportant code

   // Instantiate all remaining (non-lazy-init) singletons.
   // The most important code
   / / call the preInstantiateSingletons DefaultListableBeanFactory here
   // Instantiate all singleton objects
   beanFactory.preInstantiateSingletons();
}
Copy the code
// Really call here, emphasis
@Override
public void preInstantiateSingletons(a) throws BeansException {
   if (logger.isDebugEnabled()) {
      logger.debug("Pre-instantiating singletons in " + this);
   }

   // Iterate over a copy to allow for init methods which in turn register new bean definitions.
   // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
   // Get all the classes to be initialized from bdMap
   List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

   // Trigger initialization of all non-lazy singleton beans...
   for (String beanName : beanNames) {
      // This line of code is not important, merge parent class BD, this application is rarely used
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      // Will definitely enter if
      // If the bean is not abstract, is singleton, and is not lazily loaded, do the following
      if(! bd.isAbstract() && bd.isSingleton() && ! bd.isLazyInit()) {// This is a FactoryBean
         if (isFactoryBean(beanName)) {
            Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
            if (bean instanceof FactoryBean) {
               finalFactoryBean<? > factory = (FactoryBean<? >) bean;boolean isEagerInit;
               if(System.getSecurityManager() ! =null && factory instanceofSmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<? >) factory)::isEagerInit, getAccessControlContext()); }else {
                  isEagerInit = (factory instanceofSmartFactoryBean && ((SmartFactoryBean<? >) factory).isEagerInit()); }if(isEagerInit) { getBean(beanName); }}}else {
            / / the keygetBean(beanName); }}}}Copy the code

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

   // Because your beanName might have an ampersand, remove the ampersand
   // This is FactoryBean
   final String beanName = transformedBeanName(name);
   Object bean;

   // Eagerly check singleton cache for manually registered singletons.
   
   // Get the bean object from the spring level 3 cache
   Object sharedInstance = getSingleton(beanName);
   if(sharedInstance ! =null && args == null) {
      if (logger.isDebugEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                  "' that is not fully initialized yet - a consequence of a circular reference");
         }
         else {
            logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); }}// If the fetched Bean instance is a Bean instance of a FactoryBean, then an object instance needs to be generated from the FactoryBean instance
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }

   // Importantly, there is no instance of this object in the container
   else {
      // Fail if we're already creating this bean instance:
      // We're assumably within a circular reference.
      / * * * if it is a prototype, should not create * spring when initialization of the class is creating an identifier with CurrentlyInCreation * spring container circular dependencies error demonstrates BeanCurrentlylnCreationException * /
      if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
      }

    
      try {
          
         // Create bean instance.
         // Focus on focus
         if (mbd.isSingleton()) {
            /** * getSingleton is equivalent to fetching the object from the cache based on the beanName, and if not, calling the createBean method of the anonymous inner class below */
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  / / the key
                  / / call the AbstractAutowireCapableBeanFactory createBean method
                  return createBean(beanName, mbd, args);
               }
               catch (BeansException ex) {
                  // Explicitly remove instance from singleton cache: It might have been put there
                  // eagerly by the creation process, to allow for circular reference resolution.
                  // Also remove any beans that received a temporary reference to the bean.
                  destroySingleton(beanName);
                  throwex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }}return (T) bean;
}
Copy the code
  • transformedBeanNameConvert name to a true beanName (Name may bea FactoryBean that starts with an & character or has an alias, so it needs to be converted)
/** * get singleton beans from three maps * three caches ** Level 3 caching problem cycle dependency problem * Level 3 caching is designed to solve cycle dependency */
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   // This singletonObjects is the micro SpringIOC container
   // Get the singleton from the level 1 cache
   Object singletonObject = this.singletonObjects.get(beanName);
   IsSingletonCurrentlyInCreation / * * * * to judge whether the current single reason bean is created, but also has been to create the object instance is not yet complete attribute assignment * such as A constructor depends upon the B object so have to go to create A B object, Or the populateBean process of A depends on the B object, and the B object has to be created first * this is the state in which A is being created */
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
         // Get the singleton bean from the level 2 cache
         singletonObject = this.earlySingletonObjects.get(beanName);
         AllowEarlyReference Whether it is allowed to look up objects from singletonFactories using getObject
         if (singletonObject == null && allowEarlyReference) {
            // Get a single bean from the level 3 cacheObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName);
            if(singletonFactory ! =null) {
               singletonObject = singletonFactory.getObject();
               // Move proxy level 2 cache from level 3 cache
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName); }}}}return singletonObject;
}
Copy the code
  • And then the first one to passgetSingleton(beanName)The singletonObjects () method attempts to find if the instance sharedInstance exists from the level1 cache, and if not, callsisSingletonCurrentlyInCreation(beanName)Determine if the object is being created and what is being created? The simple understanding is that the object is instantiated, but not yet populated with properties and initialized (singletons are only created once in the same Spring container, and then fetched directly from the cache).

IsSingletonCurrentlyInCreation (beanName) method code is as follows:

public boolean isSingletonCurrentlyInCreation(String beanName) {
   return this.singletonsCurrentlyInCreation.contains(beanName);
}
Copy the code

Going back to the getSingleton(beanName) method above, try to get the object from the earlySingletonObjects cache if it is being created, or from the singletonFactory cache if it is not. When getSingleton(beanName) is first entered, none of the caches contains a beanName, so null is returned.

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

   // Because your beanName might have an ampersand, remove the ampersand
   // This is FactoryBean
   final String beanName = transformedBeanName(name);
   Object bean;

   // Eagerly check singleton cache for manually registered singletons.
   
   // Get the bean object from the spring level 3 cache
   Object sharedInstance = getSingleton(beanName);
   if(sharedInstance ! =null && args == null) {
      if (logger.isDebugEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                  "' that is not fully initialized yet - a consequence of a circular reference");
         }
         else {
            logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); }}// If the fetched Bean instance is a Bean instance of a FactoryBean, then an object instance needs to be generated from the FactoryBean instance
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }

   // Importantly, there is no instance of this object in the container
   else {
      // Fail if we're already creating this bean instance:
      // We're assumably within a circular reference.
      / * * * if it is a prototype, should not create * spring when initialization of the class is creating an identifier with CurrentlyInCreation * spring container circular dependencies error demonstrates BeanCurrentlylnCreationException * /
      if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
      }

    
      try {
          
         // Create bean instance.
         // Focus on focus
         if (mbd.isSingleton()) {
            /** * getSingleton is equivalent to fetching the object from the cache based on the beanName, and if not, calling the createBean method of the anonymous inner class below */
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  / / the key
                  / / call the AbstractAutowireCapableBeanFactory createBean method
                  return createBean(beanName, mbd, args);
               }
               catch (BeansException ex) {
                  // Explicitly remove instance from singleton cache: It might have been put there
                  // eagerly by the creation process, to allow for circular reference resolution.
                  // Also remove any beans that received a temporary reference to the bean.
                  destroySingleton(beanName);
                  throwex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }}return (T) bean;
}
Copy the code

Let’s move on to the second getSingleton method that contains the lamda expression, as follows:

public Object getSingleton(String beanName, ObjectFactory
        singletonFactory) {
   Assert.notNull(beanName, "Bean name must not be null");
   synchronized (this.singletonObjects) {
       // Try to fetch beans from level 1 cache, null at this point
      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {
         
         / / to the above said the store is creating objects in the container to add the beanName singletonsCurrentlyInCreation
         beforeSingletonCreation(beanName);
          
         try {
            // This line of code actually creates the object, the object is already a proxy object, important
            /** * This is where the post-handler turns the native into a proxy object, looking at the createBean method in the anonymous inner class. This is where the createBean method in the lamda expression is called */
            singletonObject = singletonFactory.getObject();
            After creating the object by calling the lamda expression createBean method, set the variable to true
            newSingleton = true;
         }
         catch (IllegalStateException ex) {
            
         }
         
         if (newSingleton) {
            // Level 3 caching solves the problem of loop dependency
            // After the object is instantiated and set to attributes only and initialized
            // The object is placed in the level 1 cacheaddSingleton(beanName, singletonObject); }}returnsingletonObject; }}Copy the code

The main logic of this getSingleton method is: First get from level 1 cache singletonObjects, at this time for less than is null, then the current beanName for a deposit to the above mentioned stored in the storage is creating object singletonsCurrentlyInCreation, Then call singletonFactory. GetObject (); Call the createBean method in the lamda expression to create an object with beanName a. Then set newSingleton to true. The addSingleton method is then called to add the created object to the level-1 cache singletonObjects.

The addSingleton method is as follows: Add the created complete object (i.e., the instantiated, populated, and initialized objects) to singletonObjects, then remove the second and third caches.

protected void addSingleton(String beanName, Object singletonObject) {
   synchronized (this.singletonObjects) {
      // Put the object into the level 1 cache
      this.singletonObjects.put(beanName, singletonObject);
      // Remove objects from level 3 cache
      this.singletonFactories.remove(beanName);
      // Remove objects from level 2 cache
      this.earlySingletonObjects.remove(beanName);
      this.registeredSingletons.add(beanName); }}Copy the code

Then look at the createBean method to create the object as follows:

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {

   // Omit unimportant code

   try {
      // The method to actually instantiate the bean (and populate the property, initialize it) is in this line
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      if (logger.isDebugEnabled()) {
         logger.debug("Finished creating instance of bean '" + beanName + "'");
      }
      return beanInstance;
   }
   catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
      // A previously detected exception with proper bean creation context already,
      // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
      throw ex;
   }
   catch (Throwable ex) {
      throw new BeanCreationException(
            mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex); }}Copy the code

Then look at the doCreateBean method

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {

   // Instantiate the bean.
   //BeanWrapper wraps a layer around the real object and getWrappedInstance in this class returns the real object
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      // Start creating wrapper classes for real objects, using reflection
      // The default call to construct the instantiated bean without arguments
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   // Here we take the native object from the wrapper class, not the proxy object, and then we change the native object into the proxy object
   final Object bean = instanceWrapper.getWrappedInstance();
   
   }

   // Eagerly cache singletons to be able to resolve circular references
   // even when triggered by lifecycle interfaces like BeanFactoryAware.
   /** * Key steps to resolve loop dependencies */
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
   // If you want to expose a single bean ahead of time, add the bean to the level 3 cache
   if (earlySingletonExposure) {
      if (logger.isDebugEnabled()) {
         logger.debug("Eagerly caching bean '" + beanName +
               "' to allow for resolving potential circular references");
      }
      // Important, be sure to check it out
      // Put the created object into the corresponding map
      SingleFactories (key besnName,value FactoryBean)
      //singletonFactory is obtained from the lamda expression
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
      // Assign attribute, important
      // The attributes are populated by the Common and Autowired back-end processors
      Step 2: Populate the properties (DI dependency injection occurs in this step)
      populateBean(beanName, mbd, instanceWrapper);
      /** * this is where the native object is converted into a proxy object. < bESN init-method=""> the init-method method in this step is called */
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   catch (Throwable ex) {
      if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
         throw (BeanCreationException) ex;
      }
      else {
         throw new BeanCreationException(
               mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); }}return exposedObject;
}
Copy the code

CreateBeanInstance (beanName, MBD, args); AddSingletonFactory (beanName, () -> getEarlyBeanReference(beanName, MBD, bean)); Add the “a” object to the “SingletonFactory” method:

protected void addSingletonFactory(String beanName, ObjectFactory
        singletonFactory) {
   Assert.notNull(singletonFactory, "Singleton factory must not be null");
   synchronized (this.singletonObjects) {
      // First check if there are objects in the level 1 cache
      if (!this.singletonObjects.containsKey(beanName)) {
         // First set the keyword to beanName and value to singletonFactory obtained from the lamda expression
         // Put it in level 3 cache
         this.singletonFactories.put(beanName, singletonFactory);
         this.earlySingletonObjects.remove(beanName);
         this.registeredSingletons.add(beanName); }}}Copy the code

() -> getEarlyBeanReference(beanName, MBD, bean);

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
   Object exposedObject = bean;
   if(! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {
         // provide extensibility, that is, allow objects to do other things before they are stored in level 3 cache
         if (bp instanceofSmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); }}}return exposedObject;
}
Copy the code

After adding object A to the tertiary cache singletonFactory, object A calls populateBean(beanName, MBD, instanceWrapper); Populate the properties as follows;

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
   
   // omit extraneous code

   // Get all the property property values, because the class properties are put into PropertyValues when the BeanDefinition object is assembled
   PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

   if(pvs ! =null) {
      // Based on XML
      // Complete dependency injection
      //PropertyValues PVS: set of dependent PropertyValues
      //BeanWrapper bw: wrapper object for the instantiated Bean
      // The xmL-style attributes are injected hereapplyPropertyValues(beanName, mbd, bw, pvs); }}Copy the code

Then look at the applyPropertyValues method as follows:

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
   
   // Iterate through all the attributes in class A, including the dependent class B attributes
   for (PropertyValue pv : original) {
      if (pv.isConverted()) {
         deepCopy.add(pv);
      }
      else {
         String propertyName = pv.getName();
         Object originalValue = pv.getValue();
         // Create a resolvedValue for a class B object
         //!!!! Focus on
         // The object in the bit property has not been created yet, so you cannot set the property value directly. You must create the property object first
         Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
         
   // Set our (possibly massaged) deep copy.
   try {
      // Populate the created class B object with the A object
      // Complete dependency injection
      bw.setPropertyValues(new MutablePropertyValues(deepCopy));
   }
   catch (BeansException ex) {
      
   }
}
Copy the code

The above code, the first call valueResolver. ResolveValueIfNecessary address the question of a object depend on B class will be created out of the class B object, then the last call bw. SetPropertyValues fill of class B object as properties to a object.

The resolveValueIfNecessary method creates a class B object as follows:

@Nullable
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
   // We must check each value to see whether it requires a runtime reference
   // to another bean to be resolved.
   if (value instanceof RuntimeBeanReference) {
      RuntimeBeanReference ref = (RuntimeBeanReference) value;
      // Focus on this method
      return resolveReference(argName, ref);
   }
    
   // omit extraneous code
}
Copy the code

Then look at the resolveReference method as follows:

@Nullable
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
   try {
      Object bean;
      String refName = ref.getBeanName();
      refName = String.valueOf(doEvaluate(refName));
      if (ref.isToParent()) {
         if (this.beanFactory.getParentBeanFactory() == null) {
            throw new BeanCreationException(
                  this.beanDefinition.getResourceDescription(), this.beanName,
                  "Can't resolve reference to bean '" + refName +
                        "' in parent factory: no parent factory available");
         }
         bean = this.beanFactory.getParentBeanFactory().getBean(refName);
      }
      else {
         // key!!
         // Find the property object from the Spring container
         // Create a new object
         bean = this.beanFactory.getBean(refName);
         this.beanFactory.registerDependentBean(refName, this.beanName);
      }
       
      return bean;
   }
   catch (BeansException ex) {
   }
}
Copy the code

In the above code, call this.beanFactory.getBean(refName); Create A class B object. The process for creating an object is similar to that for creating A class A object. Then look at the getBean method as follows:

doGetBean => createBean => doCreateBean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {

   
   if (instanceWrapper == null) {
      // Start creating wrapper classes for real objects, using reflection
      // The default call to construct the instantiated bean without arguments
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   // Here we take the native object from the wrapper class, not the proxy object, and then we change the native object into the proxy object
   final Object bean = instanceWrapper.getWrappedInstance();
   
   /** * Key steps to resolve loop dependencies */
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
   // If you want to expose a single bean ahead of time, add the bean to the level 3 cache
   if (earlySingletonExposure) {
      if (logger.isDebugEnabled()) {
         logger.debug("Eagerly caching bean '" + beanName +
               "' to allow for resolving potential circular references");
      }
      // Important, be sure to check it out
      // Put the created object into the corresponding map
      SingleFactories (key besnName,value FactoryBean)
      //singletonFactory is obtained from the lamda expression
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
      // Assign attribute, important
      // The attributes are populated by the Common and Autowired back-end processors
      Step 2: Populate the properties (DI dependency injection occurs in this step)
      populateBean(beanName, mbd, instanceWrapper);
      /** * this is where the native object is converted into a proxy object. < bESN init-method=""> the init-method method in this step is called */
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   catch (Throwable ex) {
 
   }   
   return exposedObject;
}
Copy the code

Class A is dependent on class A. populateBean => applyPropertyValues

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
   // Iterate over all attributes for padding, including class A objects
   for (PropertyValue pv : original) {
      if (pv.isConverted()) {
         deepCopy.add(pv);
      }
      else {
         String propertyName = pv.getName();
         Object originalValue = pv.getValue();
         // Loop dependent emphasis
         //!!!! Focus on
         // The object in the bit property has not been created yet, so you cannot set the property value directly. You must create the property object first
         Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
         
   // Set our (possibly massaged) deep copy.
   try {
      / / the key
      // Complete dependency injection
      bw.setPropertyValues(newMutablePropertyValues(deepCopy)); }}Copy the code

In the same way, find class A objects through resolveValueIfNecessary, and finally fill class A objects into class B object properties with setPropertyValues. See resolveValueIfNecessary as follows:

@Nullable
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
   // We must check each value to see whether it requires a runtime reference
   // to another bean to be resolved.
   if (value instanceof RuntimeBeanReference) {
      RuntimeBeanReference ref = (RuntimeBeanReference) value;
      // Focus on this method
      returnresolveReference(argName, ref); }}Copy the code

Similarly, check the resolveValueIfNecessary method as follows:

@Nullable
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
   try {
     
      if (ref.isToParent()) {
         
      }
      else {
         // key!!
         // Find the property object from the Spring container
         // Create a new object
         bean = this.beanFactory.getBean(refName);
         this.beanFactory.registerDependentBean(refName, this.beanName); }}Copy the code

The getBean method then goes through the same process above to get object A, getBean => doGetBean

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

   // Because your beanName might have an ampersand, remove the ampersand
   // This is FactoryBean
   final String beanName = transformedBeanName(name);
   Object bean;

   // Get the bean object from the spring level 3 cache
   Object sharedInstance = getSingleton(beanName);
   if(sharedInstance ! =null && args == null) {
       
      // If the fetched Bean instance is a Bean instance of a FactoryBean, then an object instance needs to be generated from the FactoryBean instance
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }

   // Importantly, there is no instance of this object in the container
   else {
      // Fail if we're already creating this bean instance:
      // We're assumably within a circular reference.
      /** * If it is a prototype, the flag CurrentlyInCreation in Spring should not be created at initialization time to indicate that the class is being created */
      if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
      }

         // Create bean instance.
         // Focus on focus
         if (mbd.isSingleton()) {
            /** * getSingleton is equivalent to fetching the object from the cache based on the beanName, and if not, calling the createBean method of the anonymous inner class below */
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  / / the key
                  / / call the AbstractAutowireCapableBeanFactory createBean method
                  return createBean(beanName, mbd, args);
               }
               catch(BeansException ex) { } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }}catch (BeansException ex) {
         
      }
   }
    
   return (T) bean;
}
Copy the code

Object sharedInstance = getSingleton(beanName); That is as follows:

@Nullable protected Object getSingleton(String beanName, Boolean allowEarlyReference) {// This singletonObject is the microspringIOC container // Get singletonObject = from the level 1 cache this.singletonObjects.get(beanName); IsSingletonCurrentlyInCreation / * * * * to judge whether the current single reason bean is created, but also has been to create the object instance is not yet complete attribute assignment * such as A constructor depends upon the B object so have to go to create A B object, Or A's populateBean procedure relies on B objects, First to create A B object * this is A is the state in creating * / if (singletonObject = = null && isSingletonCurrentlyInCreation (beanName)) {synchronized (this singletonObjects) {/ / from the second level cache for singleton bean singletonObject = this. EarlySingletonObjects. Get (beanName); If (singletonObject == null &&AllowearlyReference) {if (singletonObject == null &&allowearlyReference) { // Get the single bean ObjectFactory<? > singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory ! = null) { singletonObject = singletonFactory.getObject(); / / from level 3 mobile agent level 2 cache cache enclosing earlySingletonObjects. Put (beanName singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }Copy the code

At this point, the A object is moved from the third-level cache singletonFactory to the second-level cache earlySingletonObjects and the A object is returned. Go back to the above step of filling the properties of class B objects. After filling the properties of object A, initialize the complete operation of the object. Go back to the step of creating the doGetBean of object B

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

   // Because your beanName might have an ampersand, remove the ampersand
   // This is FactoryBean
   final String beanName = transformedBeanName(name);
   Object bean;

   // Get the bean object from the spring level 3 cache
   Object sharedInstance = getSingleton(beanName);
   if(sharedInstance ! =null && args == null) {
      if (logger.isDebugEnabled()) {
         
      }
      // If the fetched Bean instance is a Bean instance of a FactoryBean, then an object instance needs to be generated from the FactoryBean instance
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }

   // Importantly, there is no instance of this object in the container
   else {
      // Fail if we're already creating this bean instance:
      // We're assumably within a circular reference.
      /** * If it is a prototype, the flag CurrentlyInCreation in Spring should not be created at initialization time to indicate that the class is being created */
      if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
      }

      
         // Create bean instance.
         // Focus on focus
         if (mbd.isSingleton()) {
            /** * getSingleton is equivalent to fetching the object from the cache based on the beanName, and if not, calling the createBean method of the anonymous inner class below */
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  / / the key
                  / / call the AbstractAutowireCapableBeanFactory createBean method
                  return createBean(beanName, mbd, args);
               }
               catch (BeansException ex) {
                  
               }
            });
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }    
   return (T) bean;
}
Copy the code

Look at the second belt lambda expressions getSingleton method is as follows: at this point has been performed lambda expression than createBean method, namely the following singletonObject = singletonFactory. GetObject (); This line of code ends with addSingleton to add object B to the level-1 cache singletonObjects.

public Object getSingleton(String beanName, ObjectFactory
        singletonFactory) {
   Assert.notNull(beanName, "Bean name must not be null");
   synchronized (this.singletonObjects) {
      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {
          
         // Add the beanName to the container for the identity of the bean being created
         beforeSingletonCreation(beanName);
         boolean newSingleton = false;
         boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
         if (recordSuppressedExceptions) {
            this.suppressedExceptions = new LinkedHashSet<>();
         }
         try {
            // This line of code actually creates the object, the object is already a proxy object, important
            /** * This is where the post-handler turns the native into a proxy object, looking at the createBean method in the anonymous inner class. This is where the createBean method in the lamda expression is called */
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
         }
         catch (IllegalStateException ex) {
           
         }
         
        
         if (newSingleton) {
            // Level 3 caching solves the problem of loop dependency
            // After the object is instantiated and set to attributes only and initialized
            // The object is placed in the level 1 cacheaddSingleton(beanName, singletonObject); }}returnsingletonObject; }}Copy the code

The class B object is created and added to the level 1 cache singletonObjects. Finally, it returns to the step of filling the properties of the A object. After filling the properties, it follows the same initialization process and goes through the creation process of the object, namely the step of doGetBean. Also back to the above singletonObject = singletonFactory. GetObject (); At the end of this step, addSingleton is executed to add object A to the level 1 cache.

conclusion

Spring’s solution to loop dependencies relies on the concept of an “intermediate state” of beans, which are instantiated but not initialized… Word-wrap: break-word! Important; “> The instantiation process is created through the constructor, so if A is not created, how can it be exposed in advance, so the constructor’s cyclic dependency cannot be solved.

Spring uses level 3 caching to solve the problem of loop dependency for singletons

  • Where level 1 cache is singleton pool < singletonObjects)
  • Level 2 cache is early exposure Objects
  • The tertiary cache is the singletonFactories.

Assuming that A and B circular reference, instantiate put it in when I was A three-level cache, then filling properties, found that rely on B, the same process are instantiated in A three-level cache, and then to fill attribute when found themselves dependent on A, this time from the cache lookup of early exposure to A, no AOP agent, directly to the original object into B, After completing the initialization of B, the property is filled and initialized. At this time, after B is completed, the remaining steps of A will be completed. If there is an AOP proxy, AOP processing will be carried out to obtain the object A after the proxy, inject B, and go through the rest of the process.

General steps:

  • 1 calls the doGetBean() method and wants to get beanA, so getSingleton() is called to look for beanA from the cache

  • 2 In getSingleton(), lookup from level 1 cache, no, return NULL

  • GetSingleton () = ObjectFactory; getSingleton() = getSingleton();

  • 4 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

  • 5 enter AbstractAutowireCapableBeanFactory# doCreateBean, 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). If true, beanA will be added to level 3 cache

  • 6 Fill the attributes of beanA. At this time, it detects that beanA depends on beanB and searches for beanB

  • 7 Call the doGetBean() method as beanA did above, look for the beanB in the cache, create none, and fill the beanB with attributes

  • 8 at this point, beanB depends on beanA, so getSingleton () is called to obtain beanA and search from level 1, Level 2 and level 3 caches successively. At this time, 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

  • 9 So beanB gets beanA’s dependencies, beanB completes the instantiation and moves beanA from level 3 to level 2

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