Spring obtains single instance process

After reading this article, you will get

  • When Spring will add bean s to the third level cache and the first level cache
  • When Spring calls back various Aware interfaces, BeanPostProcessor, InitializingBean, etc

Related articles

summary

Last two articles Spring obtains single instance process (1) and Spring obtaining single instance process (2) This paper introduces the process in front of getBean, and concludes today. Let's continue to learn the following process together

Source code analysis

// All the big brothers I depend on are well
// Create bean instance.
if (mbd.isSingleton()) {

   sharedInstance = getSingleton(beanName, () -> {
      try {
         return createBean(beanName, mbd, args);
      } catch (BeansException ex) {
         // Remove the beanName from the L3 cache because it may be put in because putting it in the L3 cache can solve the circular dependency of setter s
         destroySingleton(beanName);
         throw ex;
      }
   });

   bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} 

If the bean we are creating is a singleton,

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {

   synchronized (this.singletonObjects) {
      // See if there is any in the first level cache
      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {

         // Adding beanName to singletons currentlycreation means that it is being created
         beforeSingletonCreation(beanName);
         boolean newSingleton = false;

         try {
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
         } catch (IllegalStateException ex) {
            throw ex;
         } catch (BeanCreationException ex) {
            
            throw ex;
         } finally {
            // Singletons currentlycreation is removed from this
            afterSingletonCreation(beanName);
         }
         if (newSingleton) {
            // Add to cache
            addSingleton(beanName, singletonObject);
         }
      }
      return singletonObject;
   }
}

Some unimportant code has been deleted. Let's take a look at the process

  1. Obtain the synchronization lock, and then determine whether the bean already exists in the first level cache
  2. If it doesn't exist, add beanName to singletons currentlycreation, which means it is being created
  3. Then call the ObjectFactory's getObject method of the parameter to get a bean.
  4. Finally, remove it from singletons currentlycreation, which means it has been created
  5. Finally, it is added to the first level cache and removed from the second and third level cache

The end of the whole article!!!

In fact, the real secret lies in the ObjectFactory of the parameter. From the above process, you can know a process of creating bean s by Spring

Now let's see what the ObjectFactory of the parameter does

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

   ...........
   ...........

   try {
      // Real processing logic
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      if (logger.isTraceEnabled()) {
         logger.trace("Finished creating instance of bean '" + beanName + "'");
      }
      return beanInstance;
   } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
     
      throw ex;
   } catch (Throwable ex) {
      throw new BeanCreationException(xxxx);
   }
}

It's the guy who started with do

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

   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      // Create new instances, such as factory methods, constructor auto injection, and simple initialization according to the specified bean using the corresponding policies
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   final Object bean = instanceWrapper.getWrappedInstance();
   Class<?> beanType = instanceWrapper.getWrappedClass();
   
  ..........
  .........
   // Whether it needs to be exposed in advance to solve the problem of cycle dependence
   // Is singleton & allow circular dependency & creating
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
   if (earlySingletonExposure) {
      // In order to avoid late circular dependency, the ObjectFactory that creates the instance can be added to the factory before bean initialization
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
      // Fill properties
      populateBean(beanName, mbd, instanceWrapper);
      // Call initial method
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   } catch (Throwable ex) {
      .........
   }

   ........
   .......
  

   return exposedObject;
}

The above process is roughly

  • The createBeanInstance method selects a bean creation method, which may be a factory method, a constructor or a default constructor according to your configuration and the situation of your bean. When the parameter of one constructor is another bean, it will get the bean of this parameter through the method of getBean
  • Then add the created bean to the third level cache. By default, we allow circular dependency
  • populateBean The method is that we fill in the properties, if you rely on other Spring Other bean If it's injected in this way(autowireByName autowireByType ),That's where the injection came in. He got the rest bean Also through getBean How to get

    protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
        .........
        .........
      
          if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
             autowireByName(beanName, mbd, bw, newPvs);
          }
          // Add property values based on autowire by type if applicable.
          if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
             autowireByType(beanName, mbd, bw, newPvs);
          }
        .........
        .........
    }
  • initializeBean is to call our various callback interfaces, Aware type, BeanPostProcessor, InitializingBean, and custom initialization functions

    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
       if (System.getSecurityManager() != null) {
          AccessController.doPrivileged((PrivilegedAction<Object>) () -> {. 
              // Call various Aware interfaces
             invokeAwareMethods(beanName, bean);
             return null;
          }, getAccessControlContext());
       } else {
           // Call various Aware interfaces
          invokeAwareMethods(beanName, bean);
       }
    
       Object wrappedBean = bean;
       if (mbd == null || !mbd.isSynthetic()) {
           // Call BeanPostProcessor postProcessBeforeInitialization
          wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
       }
    
       try {
           // Call InitializingBean, custom initialization method
          invokeInitMethods(beanName, wrappedBean, mbd);
       } catch (Throwable ex) {
          throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
       }
       if (mbd == null || !mbd.isSynthetic()) {
           //  Call BeanPostProcessor postProcessAfterInitialization
          wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
       }
       return wrappedBean;
    }

    In fact, the overall process is almost the same

summary

  • Find the corresponding beanName according to the name in the parameter, whether the name is an alias or the beanName of a factoryBean
  • Check whether this beanName object is included in the cache

    • First, check whether there are singleton objects in the first level cache
    • Then from the second level cache, earlySingletonObjects
    • If none of them are available, check whether there are any from the singleton factors of the three-level cache
  • If there is a bean in the cache, we still need to deal with it

    • If the bean returned in the Spring cache is factoryBean, and the user also wants a beanFactory (the prefix in parameter name is &), then we directly return
    • If the returned bean in the Spring cache is a normal bean and the user wants a normal bean, return it directly
    • If the Bean returned in the Spring cache is a factoryBean, and what the user wants is a normal bean, then we need to get this bean from factoryBean
    • In the process of getting this bean from factoryBean, we need to call the preprocessing, postprocessing and our common interface callback BeanPostProcessor
  • If there is no bean in the cache, judge whether it is of prototype type and loop dependency
  • If not, try to find the bean in the parent container
  • If the parent container does not have one, obtain the beanDefinition corresponding to the beanName to find out the dependent beanName
  • Determine whether the beanName and the dependent beanName are circular dependencies. If not, register their dependencies and call the getBean method to create the dependent beanName
  • Add beanName to singletons currentlycreation
  • Create new instances, such as factory methods, constructors, and an incomplete bean according to the specified bean using the corresponding policies
  • Add the created bean to the third level cache
  • Property filling and interface callback
  • Finally, remove it from singletons currentlycreation, which means it has been created
  • Finally, it is added to the first level cache and removed from the second and third level cache
  • Return bean to caller

In fact, the overall process is not complicated, and we can get something from it. In fact, the most frequently asked question is what Spring is doing to solve the problem of circular dependency. What's interesting is that you can see that this article is based on the Spring cycle in the official account.

Tags: Java Spring REST

Posted on Sat, 06 Jun 2020 04:00:15 -0400 by gregghealy