Spring source code analysis: the creation process of bean instances (I)

Time:2022-7-28

In the previous article, I briefly introduced the loading process of bean definitions. The main content of the following article is the creation process of bean instances.

How to create bean instances

ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
context.getBean("beanDemo");

First of all, see the above code, which uses the getBean method. Is the entry to create a bean in getBean?

From the previous content, we can know that the singleton bean can be instantiated in advance, because it can improve the efficiency of use. Prototype beans, that is, multi instance beans, are instantiated at the time of getBean. Moreover, there is no difference between the instantiation process of singleton beans and prototype beans. Both of them use the getBean method, which is also used to instantiate in advance at startup.

The bean instance creation and configuration methods are as follows:
image.png

  • How to construct the method

There are two ways to create instances through constructors, one is nonparametric constructor, the other is parametric constructor

The following is how to configure the parameterless constructor:

public class ConstructorBean {

    public ConstructorBean() {
        System.out.println ("parameterless constructor...");
    }
}

The following is the configuration method of the parameter constructor:

public class ConstructorBean {

    public ConstructorBean(String name, int age) {
        System.out.println ("constructor with parameters...");
        System.out.println(name + "_" + age);
    }
}

The output results are as follows:

image.png

public class InstantiateFactory {

    public static String getStaticFactoryMethod() {
       Return "static factory method creates bean...";
    }
}
  • Non static factory method
public class InstantiateFactory {

   public String getMemberFactoryMethod(String name) {
      Return "non static factory method creates bean...";
   }
}
  • Specify factory method

You need to inherit the factorybean interface:

public class BoyFactoryBean implements FactoryBean {

   @Override
   public Boy getObject() throws Exception {
      return new Lad("niulang");
   }

   @Override
   public Class> getObjectType() {
      return Boy.class;
   }
}

If you want to get boyfactorybean itself, you need to prefix it with “&”. Otherwise, you will return the bean in GetObject, which is the bean instance created by factorybean

Object lsfb2 = context.getBean("boyService2");
System.out.println(lsfb2);
Object lsfb4 = context.getBean("&boyService2");
System.out.println(lsfb4);
BoyFactoryBean lsfb = (BoyFactoryBean) context.getBean("&boyService2");
System.out.println(lsfb);
System.out.println(lsfb.getObject());

The outputs are as follows:

image.png

Process of instantiating beans

From the refresh () method in the abstractapplicationcontext class, there is a step to instantiate some singleton beans in advance:

//Complete the initialization of the bean factory and initialize all non lazy loaded singleton beans
finishBeanFactoryInitialization(beanFactory);

Click to view the specific implementation:

/**
 * Finish the initialization of this context's bean factory,
 * initializing all remaining singleton beans.
 */
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
   // Initialize conversion service for this context.
   //Instantiate type conversion service
   if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
         beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
      beanFactory.setConversionService(
            beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
   }

   // Register a default embedded value resolver if no bean post-processor
   // (such as a PropertyPlaceholderConfigurer bean) registered any before:
   // at this point, primarily for resolution in annotation attribute values.
   //Make sure beanfactory holds the parser of the embedded value
   if (!beanFactory.hasEmbeddedValueResolver()) {
      beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
   }

   // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
   //Instantiate loadtimeweaver aware beans in advance
   String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
   for (String weaverAwareName : weaverAwareNames) {
      getBean(weaverAwareName);
   }

   // Stop using the temporary ClassLoader for type matching.
   beanFactory.setTempClassLoader(null);

   // Allow for caching all bean definition metadata, not expecting further changes.
   //Freeze the configuration and instantiate it. The bean definition information cannot be changed
   beanFactory.freezeConfiguration();

   // Instantiate all remaining (non-lazy-init) singletons.
   //Instantiate a singleton bean
   beanFactory.preInstantiateSingletons();
}

Then enter the preinstantiatesingletons method:

/**Bean definition name collection stored in registration order*/
    private volatile List beanDefinitionNames = new ArrayList<>(256);
// 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 bean definition names
   List beanNames = new ArrayList<>(this.beanDefinitionNames);

According to the above code, as soon as this method comes in, it gets all the bean definition names. The list of bean definitions is decorated with volatile keyword. For those who are not clear about volatile, please see the previous articles on threads.

// Trigger initialization of all non-lazy singleton beans...
//Trigger the initialization of all non lazy load singleton beans
for (String beanName : beanNames) {
   //Get the merged bean definition information
   RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
   //If the bean definition is not abstract, is singleton, and is not lazy loaded, continue to execute
   if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
      //If it is a factory bean, continue to execute, otherwise directly execute the getBean method
      if (isFactoryBean(beanName)) {
         //Get the bean. The bean here is a factory bean
         Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
         if (bean instanceof FactoryBean) {
            FactoryBean> factory = (FactoryBean>) bean;
            //Judge whether it is in urgent need of instantiation
            boolean isEagerInit;
            if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
               isEagerInit = AccessController.doPrivileged(
                     (PrivilegedAction) ((SmartFactoryBean>) factory)::isEagerInit,
                     getAccessControlContext());
            }
            else {
               isEagerInit = (factory instanceof SmartFactoryBean &&
                     ((SmartFactoryBean>) factory).isEagerInit());
            }
            //If the bean needs to be instantiated urgently, call the getBean method
            if (isEagerInit) {
               getBean(beanName);
            }
         }
      }
      else {
         //Get ordinary beans
         getBean(beanName);
      }
   }
}

What is factorybean? It has been used simply. Although factorybean is also a bean, it is a bean that can produce beans. If you directly use getBean to obtain the beans it produces, you need to use getBean (& + beanname) to obtain this factory bean.

Introduction to dogetbean method

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

Four parameters are passed here. The first parameter name is the name of the bean definition, requiredtype is the type required by the bean, args is the explicitly passed parameter, and typecheckonly whether to check the type.

Args refers to the parameters in the following code:

ConstructorBean constructorBean = (ConstructorBean) context.getBean("constructorBean", new Object[]{1,2,3});

Using the example just above, and then the debug to dogetbean method, you can see:

image.png

The first step of entering the method is to execute the transformedbeanname method:

String beanName = transformedBeanName(name);
Object bean;

This transformedbeanname method is to get the standard bean definition name. What is the standard bean definition name? Because when configuring the bean definition, you can define some aliases for this bean.

When using getBean (“alias”), you can also get the bean definition. The main reason is that the underlying layer uses aliasmap. The key value of this aliasmap is the alias, and the value value is the real bean definition name.

/** Map from alias to canonical name. */
private final Map aliasMap = new ConcurrentHashMap<>(16);
/**
 * Return the bean name, stripping out the factory dereference prefix if necessary,
 * and resolving aliases to canonical names.
 * @param name the user-specified name
 * @return the transformed bean name
 */
protected String transformedBeanName(String name) {
   return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

Here, two methods, beanfactoryutils.transformedbeanname and canonicalname, are called:

/**
 *Return the bean name, and remove the factory dereference prefix if necessary
 * @param name the name of the bean
 * @return the transformed name
 * @see BeanFactory#FACTORY_BEAN_PREFIX
 */
public static String transformedBeanName(String name) {
   Assert.notNull(name, "'name' must not be null");
   //If the bean name does not contain the & prefix, it is returned directly
   if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
      return name;
   }
   //Remove the dereference prefix of the bean name, that is, the & prefix
   return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
      do {
         beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
      }
      while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
      return beanName;
   });
}
/**
 *The function of this method is to get the real bean name
 * @param name the user-specified name
 * @return the transformed name
 */
public String canonicalName(String name) {
   String canonicalName = name;
   // Handle aliasing...
   String resolvedName;
   do {
      resolvedName = this.aliasMap.get(canonicalName);
      if (resolvedName != null) {
         canonicalName = resolvedName;
      }
   }
   while (resolvedName != null);
   return canonicalName;
}

Canonicalname uses a do… While loop to get the real bean name. Here is a small example:

ConstructorBean constructorBean = (ConstructorBean) context.getBean("a");

The constructorbean is configured with four aliases bean1, bean2, bean3 and bean4, and the alias bean2 is configured with an alias a, that is, the alias of the alias.

After debugging, you can see that the alias a of alias bean2 is passed here:

image.png
The following figure shows the alias map of the bean definition alias:
image.png
Now you know why we use the do… While loop to get the standard bean name, because the alias defined by the bean can also have an alias. When resolvedname is empty, that is, there is no alias, it will jump out of the loop and get the real bean name.

Next, if… Else is divided into two parts. Let’s look at the first part:

// Eagerly check singleton cache for manually registered singletons.
//Get the instantiated singleton bean from the cache
Object sharedInstance = getSingleton(beanName);
//If the bean instance exists and the explicitly passed parameter is empty, execute the following code
if (sharedInstance != null && args == null) {
   if (logger.isTraceEnabled()) {
      if (isSingletonCurrentlyInCreation(beanName)) {
         logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
               "' that is not fully initialized yet - a consequence of a circular reference");
      }
      else {
         logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
      }
   }
   //Here, it is to judge whether this bean is a factorybean. If it is a factorybean, you need to create an instance from the factorybean
   bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}

Let’s first look at the getsingleton method:

/**Caching of singleton beans*/
private final Map singletonObjects = new ConcurrentHashMap<>(256);

/**Cache of single instance factory*/
private final Map> singletonFactories = new HashMap<>(16);

/**Cache of preloaded singleton beans*/
private final Map earlySingletonObjects = new HashMap<>(16);

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   //First, get the bean instance from the cache of the singleton bean
   Object singletonObject = this.singletonObjects.get(beanName);
   //If it is not obtained and the bean is being created, execute the following content
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      //Lock
      synchronized (this.singletonObjects) {
         //Get the bean instance from the cache of the preloaded singleton bean
         singletonObject = this.earlySingletonObjects.get(beanName);、
         //If you still don't get it, and you need to create an early reference
         if (singletonObject == null && allowEarlyReference) {
            //Get the singleton factory from beanname
            ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);
            //If the factory is not empty
            if (singletonFactory != null) {
               //Get bean
               singletonObject = singletonFactory.getObject();
               //Put it into the cache of preloaded singleton beans
               this.earlySingletonObjects.put(beanName, singletonObject);
               //Remove beans from singletonfactories,
               //Because earlysingletonobjects has been added to it, singletonfactories is not required
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return singletonObject;
}

If the above method does not get the bean instance, then follow the else method below:

// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
   throw new BeanCurrentlyInCreationException(beanName);
}

First of all, here is to judge whether the prototype bean is already under construction to detect circular dependency. For example, bean2 needs to be relied on when creating bean1, and bean1 needs to be relied on in the process of creating bean2, so an exception will be thrown.

The following code is to judge the parent factory:

// Check if bean definition exists in this factory.
//First, get the parent factory
BeanFactory parentBeanFactory = getParentBeanFactory();
//If the parent factory is not empty and the local does not contain this bean definition, get it from the parent factory
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
   // Not found -> check parent.
   //This step is to restore the original bean name, because factorybean exists,
   //Remove the prefix from it and add it back here
   String nameToLookup = originalBeanName(name);
   //The following is to create bean instances through parent classes
   if (parentBeanFactory instanceof AbstractBeanFactory) {
      return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
            nameToLookup, requiredType, args, typeCheckOnly);
   }
   else if (args != null) {
      // Delegation to parent with explicit args.
      return (T) parentBeanFactory.getBean(nameToLookup, args);
   }
   else if (requiredType != null) {
      // No args -> delegate to standard getBean method.
      return parentBeanFactory.getBean(nameToLookup, requiredType);
   }
   else {
      return (T) parentBeanFactory.getBean(nameToLookup);
   }
}

From the above code, we can see that parentbeanfactory.getbean, where the getBean method is used, is also a recursive call.

if (!typeCheckOnly) {
   markBeanAsCreated(beanName);
}

The above code is to judge. If this is not just a type check, record here that the bean has been created. The following is the markbeanascreated method:

/** Map from bean name to merged RootBeanDefinition. */
private final Map mergedBeanDefinitions = new ConcurrentHashMap<>(256);

/** Names of beans that have already been created at least once. */
private final Set alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));

protected void markBeanAsCreated(String beanName) {
   //First, judge whether the bean collection that has been created contains the bean
   if (!this.alreadyCreated.contains(beanName)) {
      //Lock
      synchronized (this.mergedBeanDefinitions) {
         //Judge again, double check lock
         if (!this.alreadyCreated.contains(beanName)) {
            // Let the bean definition get re-merged now that we're actually creating
            // the bean... just in case some of its metadata changed in the meantime.
            //Here, clear the merged bean definitions and merge them again,
            //To prevent the bean definition from changing
            clearMergedBeanDefinition(beanName);
            //Add beanname to the set that has been created
            this.alreadyCreated.add(beanName);
         }
      }
   }
}

Then enter the following code again:

RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);

// Guarantee initialization of beans that the current bean depends on.
//Get dependent beans
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
   //Loop through dependent beans
   for (String dep : dependsOn) {
      //Judge whether it is circular dependency, and throw an exception if it is
      if (isDependent(beanName, dep)) {
         throw new BeanCreationException(mbd.getResourceDescription(), beanName,
               "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
      }
      //Register dependent beans
      registerDependentBean(dep, beanName);
      try {
         //Get the dependent bean
         getBean(dep);
      }
      catch (NoSuchBeanDefinitionException ex) {
         throw new BeanCreationException(mbd.getResourceDescription(), beanName,
               "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
      }
   }
}

Here, first get the merged bean definition, and then check whether the bean is abstract. If so, an exception will be thrown. This judgment is in the checkmergedbeandefinition method.

Then, check whether this bean depends on other beans. If so, register the dependent beans in a dependent map, and then call the getBean method to instantiate the dependent beans first.

Finally, if the above judgment ends and the bean instance is not obtained, execute the following code to create the bean instance:

// Create bean instance.
//Scope processing: singleton, prototype, other
//Judge whether it is a single case
if (mbd.isSingleton()) {
   //Get the singleton bean, create it if you can't get it, and then cache it in the collection of singleton beans
   sharedInstance = getSingleton(beanName, () -> {
      try {
         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);
         throw ex;
      }
   });
   //Processing factory beans
   bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//Processing of prototype beans
else if (mbd.isPrototype()) {
   // It's a prototype -> create a new instance.
   Object prototypeInstance = null;
   try {
      //Check the circular dependency of multiple beans and make records
      beforePrototypeCreation(beanName);
      //Create bean
      prototypeInstance = createBean(beanName, mbd, args);
   }
   finally {
     //Release the record of circular dependency check
      afterPrototypeCreation(beanName);
   }
   //Processing factory beans
   bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
//Processing of other scope beans
else {
   String scopeName = mbd.getScope();
   if (!StringUtils.hasLength(scopeName)) {
      throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
   }
   Scope scope = this.scopes.get(scopeName);
   if (scope == null) {
      throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
   }
   try {
      Object scopedInstance = scope.get(beanName, () -> {
        //Circular dependency check and make records
         beforePrototypeCreation(beanName);
         try {
            //Create bean
            return createBean(beanName, mbd, args);
         }
         finally {
            //Release the record of circular dependency check
            afterPrototypeCreation(beanName);
         }
      });
      //Processing factory beans
      bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
   }
   catch (IllegalStateException ex) {
      throw new BeanCreationException(beanName,
            "Scope '" + scopeName + "' is not active for the current thread; consider " +
            "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
            ex);
   }
}

The above creation is divided into three parts: single instance, multi instance and other scope beans. The general process is the same, and the createbean method is called to create them.

Getsingleton method

Let’s take a look at the getsingleton method of the single bean:

/**
 * Return the (raw) singleton object registered under the given name,
 * creating and registering a new one if none registered yet.
 * @param beanName the name of the bean
 * @param singletonFactory the ObjectFactory to lazily create the singleton
 * with, if necessary
 * @return the registered singleton object
 */
public Object getSingleton(String beanName, ObjectFactory> singletonFactory) {}

This method is used to get the singleton bean, and will call the method of creating the bean.

synchronized (this.singletonObjects) {
   Object singletonObject = this.singletonObjects.get(beanName);
}

First, lock it, and then get the singleton bean from singletonobjects. Singletonobjects has said above, which stores the instantiated singleton bean set. Here, get it from singletonobjects first, and then create it if you can’t get it.

/** Flag that indicates whether we're currently within destroySingletons. */
private boolean singletonsCurrentlyInDestruction = false;

/** Collection of suppressed Exceptions, available for associating related causes. */
@Nullable
private Set suppressedExceptions;
//If the singleton bean cannot be obtained, create it
if (singletonObject == null) {
    //Judge whether the singleton bean is being destroyed. If so, throw an exception
    if (this.singletonsCurrentlyInDestruction) {
       throw new BeanCreationNotAllowedException(beanName,
             "Singleton bean creation not allowed while singletons of this factory are in destruction " +
             "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
    }
    if (logger.isDebugEnabled()) {
       logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
    }
    //The processing before the creation of a singleton bean is also recorded
    beforeSingletonCreation(beanName);
    //Whether to create a newly created singleton bean
    boolean newSingleton = false;
    //Record the exceptions generated during the creation process. It may be. I guess, haha
    boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
    if (recordSuppressedExceptions) {
       this.suppressedExceptions = new LinkedHashSet<>();
    }
}

Then take a look at the beforesingletoncreation method

/** Names of beans that are currently in creation. */
private final Set singletonsCurrentlyInCreation =
      Collections.newSetFromMap(new ConcurrentHashMap<>(16));

/** Names of beans currently excluded from in creation checks. */
private final Set inCreationCheckExclusions =
      Collections.newSetFromMap(new ConcurrentHashMap<>(16));

protected void beforeSingletonCreation(String beanName) {
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
   }
}

After judgment and addition, creationcheckeexclusions is a collection of excluded bean names, that is, this bean will not be created. Singletonscurrentlyincreation records the name of the bean currently being created.

The following is the code for creation:

try {
   //Singletonfactory is to execute the above bean creation code
   singletonObject = singletonFactory.getObject();
   //After the creation is completed, it is marked as a new singleton bean
   newSingleton = true;
}

Then the following code is to cache:

finally {
   if (recordSuppressedExceptions) {
      this.suppressedExceptions = null;
   }
   //Remove record
   afterSingletonCreation(beanName);
}
//If it is a new singleton bean, do some caching
if (newSingleton) {
   addSingleton(beanName, singletonObject);
}

Look at the aftersingletoncreation method, which removes the current bean from the collection being created

protected void afterSingletonCreation(String beanName) {
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
      throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
   }
}

Then the following is the cache method:

protected void addSingleton(String beanName, Object singletonObject) {
   synchronized (this.singletonObjects) {
      this.singletonObjects.put(beanName, singletonObject);
      this.singletonFactories.remove(beanName);
      this.earlySingletonObjects.remove(beanName);
      this.registeredSingletons.add(beanName);
   }
}

This method is relatively simple. The instantiated singleton beans are added to the singletonobjects collection. Because they have been instantiated, they are removed from singletonfactories and earlysingletonobjects, and they are also added to the registeredsingletons collection.

Prototype bean

Let’s look at the processing of the prototype bean:

/** Names of beans that are currently in creation. */
private final ThreadLocal prototypesCurrentlyInCreation =
      new NamedThreadLocal<>("Prototype beans currently in creation");

protected void beforePrototypeCreation(String beanName) {
   Object curVal = this.prototypesCurrentlyInCreation.get();
   if (curVal == null) {
      this.prototypesCurrentlyInCreation.set(beanName);
   }
   else if (curVal instanceof String) {
      Set beanNameSet = new HashSet<>(2);
      beanNameSet.add((String) curVal);
      beanNameSet.add(beanName);
      this.prototypesCurrentlyInCreation.set(beanNameSet);
   }
   else {
      Set beanNameSet = (Set) curVal;
      beanNameSet.add(beanName);
   }
}
protected void afterPrototypeCreation(String beanName) {
   Object curVal = this.prototypesCurrentlyInCreation.get();
   if (curVal instanceof String) {
      this.prototypesCurrentlyInCreation.remove();
   }
   else if (curVal instanceof Set) {
      Set beanNameSet = (Set) curVal;
      beanNameSet.remove(beanName);
      if (beanNameSet.isEmpty()) {
         this.prototypesCurrentlyInCreation.remove();
      }
   }
}

From the above source code, we can see that prototypescurrentlyincreation uses ThreadLocal to avoid repeated bean creation by threads. Afterprototypecreation removes this flag. The processing of scope and prototype bean is the same.

Getobjectforbeaninstance method

Finally, there is another method that I haven’t seen. Let’s take a look at the getobjectforbeaninstance method.

protected Object getObjectForBeanInstance(
      Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {}

Four parameters are passed here. Beaninstance is the bean instance, name is the original bean name, that is, it may contain a prefix, beanname is the standard bean name, and MBD is the merged bean definition information.

/** Package-visible field for caching if the bean is a factory bean. */
@Nullable
volatile Boolean isFactoryBean;
//Determine whether it is a factory bean
if (BeanFactoryUtils.isFactoryDereference(name)) {
   //If it is a nullbean, return directly
   if (beanInstance instanceof NullBean) {
      return beanInstance;
   }
   //If it is not factorybean type, throw an exception directly
   //It is a factory bean, but not a factorybean type
   if (!(beanInstance instanceof FactoryBean)) {
      throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
   }
   //Set the isfactorybean flag, which indicates that the bean is a factory bean
   if (mbd != null) {
      mbd.isFactoryBean = true;
   }
   //Return bean instance
   return beanInstance;
}

The method to determine whether it is a factory bean is relatively simple. It is to determine whether it starts with -:

public static boolean isFactoryDereference(@Nullable String name) {
   return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}

If the current bean instance is not of factorybean type, directly return

// Now we have the bean instance, which may be a normal bean or a FactoryBean.
// If it's a FactoryBean, we use it to create a bean instance, unless the
// caller actually wants a reference to the factory.
if (!(beanInstance instanceof FactoryBean)) {
   return beanInstance;
}

If the bean instance is not obtained above, execute the following code:

/** Cache of singleton objects created by FactoryBeans: FactoryBean name to object. */
private final Map factoryBeanObjectCache = new ConcurrentHashMap<>(16);
Object object = null;
if (mbd != null) {
   mbd.isFactoryBean = true;
}
else {
   //Get beans from cache
   object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
   // Return bean instance from factory.
   FactoryBean> factory = (FactoryBean>) beanInstance;
   // Caches object obtained from FactoryBean if it is a singleton.
   //If the bean definition is empty and the bean name is included in the beandefinitionmap, get the merged bean definition
   if (mbd == null && containsBeanDefinition(beanName)) {
      mbd = getMergedLocalBeanDefinition(beanName);
   }
   //Whether it is a user-defined bean
   boolean synthetic = (mbd != null && mbd.isSynthetic());
   //Get beans from factorybeans
   object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;

The getcachedobjectforfactorybean method is relatively simple, which can be obtained directly from the cached map:

@Nullable
protected Object getCachedObjectForFactoryBean(String beanName) {
   return this.factoryBeanObjectCache.get(beanName);
}

Then there is getobjectfromfactorybean, which obtains objects from factorybean:

protected Object getObjectFromFactoryBean(FactoryBean> factory, String beanName, boolean shouldPostProcess)

Here, three parameters are passed. Factory is the factorybean passed above, beanname is the bean name defined in the configuration, and shouldpostprocess means: whether to allow beans to be post processed.

if (factory.isSingleton() && containsSingleton(beanName)) {
  synchronized (getSingletonMutex()) {
   }
}

The first step is to judge whether the factory is a singleton or not, and whether the collection of singleton beans contains the bean name. If all are satisfied, execute the following code.

/**Cache of singleton objects created by factorybean: the factorybean name of the object*/
private final Map factoryBeanObjectCache = new ConcurrentHashMap<>(16);
//Get from cache first
Object object = this.factoryBeanObjectCache.get(beanName);
//If it does not exist in the cache, continue to execute
if (object == null) {
   object = doGetObjectFromFactoryBean(factory, beanName);
   // Only post-process and store if not put there already during getObject() call above
   // (e.g. because of circular reference processing triggered by custom getBean calls)
   //Get it from the cache again. Because of circular reference, this bean may already be in the cache
   Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
   //Get it and assign it directly
   if (alreadyThere != null) {
      object = alreadyThere;
   }
   else {
      if (shouldPostProcess) {
          //The current bean is creating a direct return, and post-processing will not be performed for the time being
         if (isSingletonCurrentlyInCreation(beanName)) {
            // Temporarily return non-post-processed object, not storing it yet..
            return object;
         }
         //Circular dependency check and make records
         beforeSingletonCreation(beanName);
         try {
            //Post process this bean
            object = postProcessObjectFromFactoryBean(object, beanName);
         }
         catch (Throwable ex) {
            throw new BeanCreationException(beanName,
                  "Post-processing of FactoryBean's singleton object failed", ex);
         }
         finally {
            //Circular dependency check, remove
            afterSingletonCreation(beanName);
         }
      }
      //Whether the current bean is in the collection of singleton beans. If so, cache it
      if (containsSingleton(beanName)) {
         this.factoryBeanObjectCache.put(beanName, object);
      }
   }
}
//Return bean instance
return object;

Then there is the processing in else. The amount of code is small, and the same code already exists in if.

Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
   try {
      object = postProcessObjectFromFactoryBean(object, beanName);
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
   }
}
return object;

Dogetobjectfromfactorybean method:

private Object doGetObjectFromFactoryBean(FactoryBean> factory, String beanName) throws BeanCreationException {
   Object object;
   try {
      //Verify permissions
      if (System.getSecurityManager() != null) {
         AccessControlContext acc = getAccessControlContext();
         try {
            object = AccessController.doPrivileged((PrivilegedExceptionAction) factory::getObject, acc);
         }
         catch (PrivilegedActionException pae) {
            throw pae.getException();
         }
      }
      else {
         //Here, the GetObject method is called directly
         object = factory.getObject();
      }
   }
   catch (FactoryBeanNotInitializedException ex) {
      throw new BeanCurrentlyInCreationException(beanName, ex.toString());
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
   }

   // Do not accept a null value for a FactoryBean that's not fully
   // initialized yet: Many FactoryBeans just return null then.
   if (object == null) {
      if (isSingletonCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(
               beanName, "FactoryBean which is currently in creation returned null from getObject");
      }
      object = new NullBean();
   }
   return object;
}

This part of the code is to get the bean instance in GetObject from factorybean.

Create instance flowchart

Next, according to the above source code content, make a simple flow chart to facilitate summary and review:

image.png

So far, the content related to getBean has been introduced. The following is the process of creating a bean instance. This article aims to record the learning content and share. If there are deficiencies, please forgive me. If there are mistakes in this article, I hope the boss will not hesitate to give advice, correct them in time, learn together, make progress together, and encourage each other!

Recommended Today

JS generate guid method

JS generate guid method https://blog.csdn.net/Alive_tree/article/details/87942348 Globally unique identification(GUID) is an algorithm generatedBinaryCount Reg128 bitsNumber ofidentifier , GUID is mainly used in networks or systems with multiple nodes and computers. Ideally, any computational geometry computer cluster will not generate two identical guids, and the total number of guids is2^128In theory, it is difficult to make two […]