The circular dependency of spring bean s is completely mastered

I Clarify the problems that need to be clarified

  1. The acyclic process of ordinary beans depends on the bean life cycle
  2. The non cyclic dependency of aop proxy bean is the process of bean life cycle
  3. The cycle of ordinary beans depends on the process of bean life cycle
  4. The circulation of aop proxy bean depends on the process of bean life cycle

It's easy to figure out the four questions and then answer the circular dependency of spring bean s

Step by step understanding is also easy to compare and understand

II The acyclic process of ordinary beans depends on the bean life cycle

1. Get bean

AbstractApplicationContext#getBean("a", A.class)

AbstractBeanFactory#doGetBean("a", A.class)

DefaultSingletonBeanRegistry#getSingleton("a", true) / / try to get the reference from the cache. True means that it is allowed to get the reference from the factory pool in advance. When getSingleton("a", true) is called for the second time in the same process, the early reference method in the factory method will be triggered and the proxy judgment will be triggered. Therefore, the early reference logic includes aop proxy generation logic, That is, referencing in advance does not necessarily generate an agent, but generating an agent in advance must be referenced in advance

If you can get the object from the cache, you don't have to complete the subsequent instantiation, property filling, initialization and aop proxy, because no matter what level of cache the object is obtained from, the subsequent steps are either completed (obtained from the first level cache), or the initialization is completed after the first property filling of the getBean() method of the bean, Anyway, other beans should not trigger getBean() to complete the subsequent initialization of the beans obtained from the cache

Time to add to semi-finished product pool

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); // Add to semi-finished product pool 
               this.singletonFactories.remove(beanName); // Remove from factory pool
            }
         }
      }
   }
   return singletonObject;
}

getSingleton method with createBean

// AbstractBeanFactory#doGetBean("a", A.class)
sharedInstance = getSingleton(beanName, () -> { // ObjectFactory.getObject()
   try {
      return createBean(beanName, mbd, args);
   }
}

2. Instantiation

AbstractAutowireCapableBeanFactory#doCreateBean

AbstractAutowireCapableBeanFactory#createBeanInstance: create instance

Add singleton factory pool

The only way to create an instance is to put the factory method applied in advance into the factory pool before property filling

if (earlySingletonExposure) {
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// **Add singleton factory pool**
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory); // Add to project pool
            this.earlySingletonObjects.remove(beanName); // Removal from semi-finished product pool
            this.registeredSingletons.add(beanName); // Register the container of the singleton beanName
        }
    }
}

AbstractAutowireCapableBeanFactory#populateBean: Property fill

AbstractAutowireCapableBeanFactory#initializeBean: initializing

try {
   populateBean(beanName, mbd, instanceWrapper); // Attribute filling, key points of circular dependency
   exposedObject = initializeBean(beanName, exposedObject, mbd); // initialization
}

3. Attribute filling

AbstractAutowireCapableBeanFactory#populateBean: Property fill

Attribute filling in annotation form

for (BeanPostProcessor bp : getBeanPostProcessors()) {
   if (bp instanceof InstantiationAwareBeanPostProcessor) {
      InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
       // Property filling code of javaconfig configuration
      PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
      if (pvsToUse == null) {
         if (filteredPds == null) {
            filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
         }
         pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
         if (pvsToUse == null) {
            return;
         }
      }
      pvs = pvsToUse;
   }
}

Attribute filling entry in xml form

if (pvs != null) {
   applyPropertyValues(beanName, mbd, bw, pvs);// Attribute filling code of xml configuration
}

4. Initialization

AbstractAutowireCapableBeanFactory#initializeBean: initializing

AbstractAutowireCapableBeanFactory#invokeAwareMethods: Aware interface call

private void invokeAwareMethods(final String beanName, final Object bean) {
   if (bean instanceof Aware) {
      if (bean instanceof BeanNameAware) {
         ((BeanNameAware) bean).setBeanName(beanName); // beanName
      }
      if (bean instanceof BeanClassLoaderAware) {
         ClassLoader bcl = getBeanClassLoader();
         if (bcl != null) {
            ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); // ClassLoader
         }
      }
      if (bean instanceof BeanFactoryAware) { // beanFactory
         ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
      }
   }
}

BeanPostProcessor#postProcessBeforeInitialization: call the post processor before initialization

Apply this BeanPostProcessor to a given new bean instance before any bean initialization callback, such as the afterpropertieset of InitializingBean or the custom init method

Abstractautowirecapablebeanfactory #invokeinitialmethods: initialization methods

InitializingBean's afterpropertieset or custom init method (@ PostConstructor or init method in xml configuration)

Call initializingbean After propertiesset then calls the custom initialization method

BeanPostProcessor#postProcessAfterInitialization: call the post processor after initialization

After any bean initialization callback (such as the afterpropertieset of InitializingBean or the custom init method), apply this BeanPostProcessor to the given new bean instance

AbstractAutoProxyCreator#postProcessAfterInitialization: method of handling aop implementation

this.earlyProxyReferences.remove(cacheKey) != bean // If it has been referenced in advance, the same bean instance will be returned, and then the proxy will not be generated repeatedly

5. Destruction method of registered one-time bean

AbstractBeanFactory#registerDisposableBeanIfNecessary

Register in the container of the one-time bean and register the destroy method

public void registerDisposableBean(String beanName, DisposableBean bean) {
	synchronized (this.disposableBeans) {
		this.disposableBeans.put(beanName, bean);
	}
}

When to add to the singleton pool

DefaultSingletonBeanRegistry#addSingleton

protected void addSingleton(String beanName, Object singletonObject) {
   synchronized (this.singletonObjects) {
      this.singletonObjects.put(beanName, singletonObject);
      this.singletonFactories.remove(beanName); // Remove from factory pool
      this.earlySingletonObjects.remove(beanName); // Remove from semi-finished product pool
      this.registeredSingletons.add(beanName); // Register the single instance beanName, Set set Set. Because it is called repeatedly, it must be a Set set Set
   }
}

In this scenario, no bean s enter the semi-finished product pool

III The non cyclic dependency of aop proxy bean is the process of bean life cycle

  1. Get bean
  2. instantiation
  3. Attribute filling
  4. initialization
  5. Destruction method of registered one-time bean

Only when initializing, the process of calling the post processor after initialization is different. Other processes are the same as "the mid-term declaration of ordinary acyclic bean s". The post processor after initialization needs to generate an aop agent, return the agent, and finally put it directly into the singleton pool

In this scenario, no bean s enter the semi-finished product pool

IV The cycle of ordinary beans depends on the process of bean life cycle

The overall process is similar to the life cycle of ordinary beans. The difference is that property filling will trigger getBean operations of other beans

  1. Get bean a(getBean(a))

  2. Instantiation a

  3. a attribute filling

    1. Trigger getBean(b)

    2. Instantiation b

    3. b attribute filling

      Trigger getBean(a): at this time, the factory pool hits, returns an ordinary bean instance, and completes the filling

    4. b initialization

    5. Method of one-time destruction of bean registration b

  4. a initialization

  5. a destruction method of registering one-time bean s

In this scenario, ordinary bean s enter the semi-finished product pool

V The circulation of aop proxy bean depends on the process of bean life cycle

Similar to the ordinary bean circular dependency scenario, the difference is that the factory method in the factory pool generates the aop proxy in advance, and there is no need to generate the proxy again after initialization

  1. Get bean a(getBean(a))

  2. Instantiation a

  3. a attribute filling

    1. Trigger getBean(b)

    2. Instantiation b

    3. b attribute filling

      Trigger getBean(a): at this time, the factory pool hits and returns the bean instance of aop proxy to complete the filling

    4. b initialization

      However, instance b still generates aop proxy in * * post processor after initialization "* * because b is not the getBean that occurs in the cycle and will not hit the factory pool

    5. b destruction method of registering one-time bean s

  4. a initialization

    **"Post processor after initialization" * * will no longer generate aop agent

  5. a destruction method of registering one-time bean s

In this scenario, the proxy bean enters the semi-finished product pool

Vi Role of L3 cache

Summarize the timing of adding and removing L3 cache

When obtaining beans: the time to add the semi-finished product pool (only triggered when there is a circular dependency and there is a factory method in the factory bean)

After the instance is created and before the attribute is filled: when the factory pool is added, each bean that needs to be instantiated will execute this logic

After the initialization and one-time bean destruction method registration are completed: when the singleton pool is added, the bean instance is returned by the method, so the semi-finished product pool and factory pool are directly removed

Therefore, the bean reference will exist in the pool as follows

Therefore, we know that the bean instance reference must enter the factory pool, and finally into the singleton pool. Only in case of circular dependency can it enter the semi-finished product pool

Role of L3 cache

Singleton pool function: solve the problem of obtaining incomplete bean s

Role of semi-finished product pool: solve circular dependency and avoid multiple execution of aop agent

Function of factory pool: when creating an instance, you don't need to care about whether you are referenced circularly and whether you are represented (the premise of considering whether you are represented is circular reference) In case of circular dependency, the generation agent is handed over to the processing when the circular dependency occurs, so as to ensure the logical consistency of what cycle in a scenario

When a bean is first used by getBean, its execution process is always like this

Get bean > instantiate bean > bean attribute filling > initialize bean > post processor after initialization: if the agent has not been generated, aop bean needs to generate agent for it > register the destruction method of singleton bean

In addition, the method is put in the factory pool, not the bean instance, so a separate cache must also be used

Keywords: Java Spring Back-end

Added by broomstick on Sun, 20 Feb 2022 13:13:52 +0200