Spring Source Learning Alas, cooking by recipe is similar to AbstractAutowire Capable BeanFactory. CreateBean process.

I remember when I was in love with my wife, one day I came to cook for my wife with a whim, and did it step by step according to the recipe. As a result, I almost burned the kitchen!!! My wife still remembers this.

Entrance

As mentioned in the previous article, the main process of AbstractBeanFactory.getBean. Today, let's talk about the createBean method of AbstractBeanFactory. The program entry is as follows:

/**.
 * The core method of this class is to create a bean instance, populate the bean instance, perform post-processing, and so on.
 * @see #doCreateBean  See doCreateBean for details
 */
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {
	  //.....
}

According to the annotation core logic in doCreateBean, let's talk about doCreateBean.

doCreateBean Logic Description

Main stream Analogous cooking by recipe
1. Instantiate Bean Bean Definition - > Bean Wrapper (if it's a singleton, try to get Bean Wrapper clearly from the cache first) Find recipes and try to get them from the collection first.
2. Processing MergedBean Definition Post Processors Integrate the wife's requirements into the recipe. For example: less salt
3. Allow the exposure of ObjectFactory in advance to solve the problem of circular references (must satisfy: singleton & allow circular references & corresponding bean s are created) Tell your wife about the taste of the dishes in advance. It's convenient for her to prepare drinks. It's also convenient for her to find dishes in advance.
4. Filling attributes Stir fry
5. Executing init-method method method Try to eat
6. There are other beans that depend on the current complete beans (properties must be filled out), remove these beans, and throw exceptions that cannot be removed When you find that the plate you prepared is too small, replace it with a new one.
7. Register Disposable Bean Interface or destroy-method Be a good man, remember to wash the dishes after dinner

Source comments at the end!!

A picture is worth a thousand words (sequence chart)

Additional explanation

1. Instantiate Bean Bean Definition - > Bean Wrapper process

Bean Derfinition has been introduced in the previous article( The Passage Gate of the Last blog Below is Bean Wrapper. Springframework. beans, BeanWrapper's annotations translated as follows:

BeanWrapper is the core interface of the underlying Java Bean in Spring, which is not usually used directly, but is used by BeanFactory and DataBinder to provide analysis and operation of standard javabeans. It can acquire, set attribute values and obtain descriptors of attribute values for querying readable and writable attributes. Supports infinite nested attributes. BeanWrapper does not support editing old values of attributes by default, so as to avoid the side effect of getter being called by setting extractOldValueForEditor to true, which can open editing old values of attributes.

Knowing BeanWrapper makes it easy to guess the general logic of BeanDefinition - > BeanWrapper

BeanDefinition - > BeanWrapper converts the metadata of a bean into a bean, but the bean does not assign attributes.
- Wen Anshi's random summary in 190921

createBeanInstance source code:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
   // Confirm the class of the bean, which has been parsed at this time
   Class<?> beanClass = resolveBeanClass(mbd, beanName);
   if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
            "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
   }
   Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
   if (instanceSupplier != null) {
      return obtainFromSupplier(instanceSupplier, beanName);
   }

   if (mbd.getFactoryMethodName() != null) {
      return instantiateUsingFactoryMethod(beanName, mbd, args);
   }
   // When judging whether to recreate the same bean
   boolean resolved = false;
   boolean autowireNecessary = false;
   if (args == null) {
      synchronized (mbd.constructorArgumentLock) {
         if (mbd.resolvedConstructorOrFactoryMethod != null) {
            resolved = true;
            autowireNecessary = mbd.constructorArgumentsResolved;
         }
      }
   }
   if (resolved) {
      if (autowireNecessary) {
         return autowireConstructor(beanName, mbd, null, null);
      }
      else {
         return instantiateBean(beanName, mbd);
      }
   }
   // Auto-injection constructor, candidate
   Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
   if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
         mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
      return autowireConstructor(beanName, mbd, ctors, args);
   }
   // Get the preferred constructor, probably the default constructor
   ctors = mbd.getPreferredConstructors();
   if (ctors != null) {
      return autowireConstructor(beanName, mbd, ctors, null);
   }
   // No special treatment, simple, practical, parametric constructor
   return instantiateBean(beanName, mbd);
}


As we can see, there are three ways to handle this createBean Instance

  • Instantiate UsingFactory Method is instantiated according to factory method
  • autowireConstructor constructor injection, constructor selection approximate logic:
    1. If there are only default constructors, no parameters specified, and no constructor parameters in BeanDefinition, use the default constructor.
    2. If there are multiple constructors, the public method is better than the private method for multiple constructors. In the case of the same visibility, the more parameters are better than the less parameters. Generally, the loose matching mode (which can be set to strict matching) is adopted, and the matching of the original value of the parameters is preferred, followed by the matching of the type after the conversion of the parameters, and finally, the matching of the original value of the parameters is not matched. If there is matching consistency, select the first matching constructor.
  • instantiateBean instantiates bean s, using the default construction method.

Let's look at the logic of instantiateBean.

Generally, the method of generating instances by reflection is as follows:

public static void main(String[] args) throws Exception{
      Class<CouponApplicationTests> clazz=CouponApplicationTests.class;
      clazz.newInstance();
      Constructor constructorToUse = clazz.getDeclaredConstructor();
      constructorToUse.newInstance()
}


How Spring is implemented is basically similar. Looking at the source code of instantiateBean, we find that its core logic is delegated to InstantiationStrategy's instantiate method. Spring 5.X's default Instantiation Strategy is CglibSubclassing Instantiation Strategy, and instantiate's implementation is not in CglibSubclassing Instantiation Strategy, but in its parent SimpleInstantiation Strategy. As follows:

@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
   // Don't override the class with CGLIB if no overrides.
   // If there is no override method, there is no CGLIB rewrite
   if (!bd.hasMethodOverrides()) {
      Constructor<?> constructorToUse;
      synchronized (bd.constructorArgumentLock) {
         constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
         if (constructorToUse == null) {
            final Class<?> clazz = bd.getBeanClass();
            if (clazz.isInterface()) {
               throw new BeanInstantiationException(clazz, "Specified class is an interface");
            }
            try {
               if (System.getSecurityManager() != null) {
                  constructorToUse = AccessController.doPrivileged(
                        (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
               }
               else {
                  constructorToUse = clazz.getDeclaredConstructor();
               }
               bd.resolvedConstructorOrFactoryMethod = constructorToUse;
            }
            catch (Throwable ex) {
               throw new BeanInstantiationException(clazz, "No default constructor found", ex);
            }
         }
      }
	  //That is to execute constructorToUse.newInstance()
      return BeanUtils.instantiateClass(constructorToUse);
   }
   else {
      // Must generate CGLIB subclass.
      return instantiateWithMethodInjection(bd, beanName, owner);
   }
}


Without Method Override, the general logic would be similar to what we wrote. But it's different if you have Method Override.
When I saw this, I was curious about hasMethodOverrides and instantiateWithMethodInjection. Looking at the name, I guess it was the @Override annotation. But looking at the source code, this MethodOverrides refers to replace-method and lookup-method. This 2 configuration, which I haven't used, queried the source code and blog on the Internet. Simply put, this is where Cglib is used to implement method injection. You can refer to the description of lookup-method and replace-method.

2. What is circular reference? How does Spring solve this problem?

What is a circular reference, for example: BeanA refers to BeanB, and BeanB also refers to BeanA

@Component
class BeanA{
   @Autowired
   BeanB beanB;
}
@Component
class BeanB{
   @Autowired
   BeanA beanA;
}

Spring can only solve single-case loops, and its solution is to expose ObjectFactory ahead of time, exposing bean s with unfilled attributes ahead of time. The flow chart is as follows:

A long winded sentence

Writing blog s is a lot harder than looking at the source code. I always try to use examples in my life to describe the main process, with a little additional explanation. Using cooking as a metaphor for Spring Source Code, I feel that although I don't know the source code thoroughly, my appetite is much better.

Cooking metaphor source code
The outline became clearer and clearer
Understanding is not thorough
The belly croaks
Have a good meal
Let's talk next time.

The next populateBean solution is expected by October 2

Source notes

/**
 * Actually create the specified bean. Pre-creation processing has already happened
 * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
 * <p>Differentiates between default bean instantiation, use of a
 * factory method, and autowiring a constructor.
 * Create the specified Bean, at which point the pre-creation preprocessing has been executed (see postProcess BeforeInstantiation).
 * Distinguish default bean instantiation, factory method, and auto-inject constructor.
 * @param beanName the name of the bean
 * @param mbd the merged bean definition for the bean
 * @param args explicit arguments to use for constructor or factory method invocation
 * @return a new instance of the bean
 * @throws BeanCreationException if the bean could not be created
 * @see #instantiateBean
 * @see #instantiateUsingFactoryMethod
 * @see #autowireConstructor
 */
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {

   // Instantiate the bean.
   // 1. Instantiated bean s
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      //Cache of unfinished FactoryBean instances from FactoryBean name to BeanWrapper.
      //Remove instances from the cache of incomplete bean s
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      //Create Bean instances to replace BeanDefinition with BeanWrapper
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   final Object bean = instanceWrapper.getWrappedInstance();
   Class<?> beanType = instanceWrapper.getWrappedClass();
   if (beanType != NullBean.class) {
      mbd.resolvedTargetType = beanType;
   }
   // Allow post-processors to modify the merged bean definition.
   synchronized (mbd.postProcessingLock) {
      //Bean
      if (!mbd.postProcessed) {
         try {
            //@ AutoWired annotation here, application
            //2. Processing MergedBean Definition Post Processors
            applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
         }
         catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                  "Post-processing of merged bean definition failed", ex);
         }
         mbd.postProcessed = true;
      }
   }
   // Eagerly cache singletons to be able to resolve circular references
   // even when triggered by lifecycle interfaces like BeanFactoryAware.
   // 3. Allow early exposure of ObjectFactory to solve circular reference problems
   //  Singleton, allowing circular references, corresponding bean s are created
   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");
      }
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   // Initialize the bean instance.
   // Instance bean s
   Object exposedObject = bean;
   try {
      //4. Filling attributes
      populateBean(beanName, mbd, instanceWrapper);
      //5. Executing init-method method method
      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);
      }
   }
   //6. There are other beans that depend on the current complete beans (overfilled attributes), remove these beans, and throw exceptions that cannot be removed
   if (earlySingletonExposure) {
      Object earlySingletonReference = getSingleton(beanName, false);
      if (earlySingletonReference != null) {
         if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
         }
         else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            //It is not allowed to inject beans that are not fully loaded, and there are other beans that depend on the current bean
            String[] dependentBeans = getDependentBeans(beanName);
            Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
            for (String dependentBean : dependentBeans) {
               //Remove other beans that depend on the current bean
               if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                  actualDependentBeans.add(dependentBean);
               }
            }
            if (!actualDependentBeans.isEmpty()) {
               //There are other beans, depending on the current beans, that have not been removed
               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.");
            }
         }
      }
   }
   // Register bean as disposable.
   try {
      //7. If you implement the Disposable Bean interface or provide destroy-method to register here
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
   }
   return exposedObject;
   }


Tags: Programming Spring less Attribute Java

Posted on Sat, 21 Sep 2019 02:59:19 -0700 by boiy