Spring instantiation source code analysis of Bean instantiation (12)

Foreword

This chapter begins by analyzing the finishBeanFactoryInitialization(beanFactory) method, which literally means completing the initialization of the Bean factory, and in the middle is the instantiation process of the non-lazy singleton Bean. ConversionService has been analyzed in advance in Chapter 10. The key point is the last sentence, and our bean instantiation analysis starts from here.

This chapter is mainly an analysis of the instantiation process and will not go too deep into the details.

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {<!-- -->
// Initialize conversion service for this context.
// ConversionService (conversion service) is used to handle type conversion tasks in the spring framework. It provides a unified way to perform conversion operations between various types,
//Including conversion of strings to other types, conversion of date and time, conversion of numeric types, etc.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) & amp; & amp;
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 BeanFactoryPostProcessor
// (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {<!-- -->
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}

// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
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.
beanFactory.freezeConfiguration();

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

Preprocessing singleton bean

preInstantiateSingletons() is a method of the Spring container that is used to pre-instantiate all singleton beans.

@Override
public void preInstantiateSingletons() throws BeansException {<!-- -->
if (logger.isTraceEnabled()) {<!-- -->
logger.trace("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.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {<!-- -->
// Get the definition information of the bean through the name of the bean
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// Not an abstract class & amp; & amp; It is a singleton & amp; & amp; It is not lazy loading
if (!bd.isAbstract() & amp; & amp; bd.isSingleton() & amp; & amp; !bd.isLazyInit()) {<!-- -->
if (isFactoryBean(beanName)) {<!-- -->
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {<!-- -->
FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null & amp; & amp; factory instanceof SmartFactoryBean) {<!-- -->
isEagerInit = AccessController.doPrivileged(
(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {<!-- -->
isEagerInit = (factory instanceof SmartFactoryBean & amp; & amp;
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {<!-- -->
getBean(beanName);
}
}
}
else {<!-- -->
getBean(beanName);
}
}
}

// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {<!-- -->
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {<!-- -->
StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
.tag("beanName", beanName);
SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {<!-- -->
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {<!-- -->
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {<!-- -->
smartSingleton.afterSingletonsInstantiated();
}
smartInitialize.end();
}
}
}

The instantiation of this preprocessed singleton bean is actually very simple. Let’s briefly analyze the overall process:

  • Get all current beanDefinitionNames for iteration.
  • Loop through all beanDefinitionNames, which is beanNamess
  • Obtain the definition information of beanName for subsequent judgment
  • If it is an abstract class, or it is not a singleton or a lazy-loaded bean, it will not be processed.
  • In other cases, the getBean logic will be entered. This method is often used to obtain a Bean from BeanFactory, and the initialization process is also encapsulated in this method.

getBean/doGetBean

Many times in Spring source code, what really does things starts with do, personal experience.

@Override
public Object getBean(String name) throws BeansException {<!-- -->
return doGetBean(name, null, null, false);
}

transformedBeanName

Get the beanName. If it is FactoryBean, it will start with & amp;, which means that what is returned here is the simple beanName.

String beanName = transformedBeanName(name);

public static String transformedBeanName(String name) {<!-- -->
Assert.notNull(name, "'name' must not be null");
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {<!-- -->
return name;
}
return transformedBeanNameCache.computeIfAbsent(name, beanName -> {<!-- -->
do {<!-- -->
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
return beanName;
});
}

getSingleton

This is what is often called Level 3 cache. When instantiating, first go to the third-level cache to check whether it exists. Do you need to go through the subsequent instantiation process?

Object sharedInstance = getSingleton(beanName);

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {<!-- -->
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null & amp; & amp; isSingletonCurrentlyInCreation(beanName)) {<!-- -->
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null & amp; & amp; allowEarlyReference) {<!-- -->
synchronized (this.singletonObjects) {<!-- -->
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {<!-- -->
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {<!-- -->
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {<!-- -->
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}

From the above code, we know that there are three Map objects, which is what we often call the third-level cache. In fact, it is the following three Maps.

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
  • Level 1 cache: singletonObjects caches singleton objects, the key is beanName and the value is the instance of the bean.
  • Second level cache: earlySingletonObjects caches early singleton objects, the key is beanName and the value is the bean instance.
  • Level 3 cache: singletonFactories cache ObjectFactory, the key is beanName and the value is ObjectFactory.

Back to the getSingleton method, first spring tries to obtain the beanName from the first-level cache. If it does not exist in the first-level cache and the current bean is not being created, it returns null directly. Only when the bean is being created will it be retrieved from the second-level cache. If it cannot be obtained from the second-level cache, it will be retrieved from the third-level cache. Here we have a general concept, which will be described in detail in the circular dependency chapter.

If the currently obtained sharedInstance is not empty and is not FacotyBean, it will be returned directly. If it is empty, continue with the subsequent logic.

dependsOn

Get whether there are dependent beans according to the BeanDefinition, and if so, instantiate other beans first. The role of the @DependsOn annotation can be reflected here.

String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {<!-- -->
for (String dep : dependsOn) {<!-- -->
if (isDependent(beanName, dep)) {<!-- -->
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular-on relationship depends between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {<!-- -->
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {<!-- -->
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}

singletonsCurrentlyInCreation

If it is a singleton, the getSingleton(String beanName, ObjectFactory singletonFactory) method will be called.

  • The first parameter is beanName
  • The second parameter is a FunctionalInterface
if (mbd.isSingleton()) {<!-- -->
// First execute the getSingleton method, and then call the createBean method in the code
sharedInstance = getSingleton(beanName, () -> {<!-- -->
try {<!-- -->
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {<!-- -->
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

Execute the getSingleton method, the code is as follows

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) {<!-- -->
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 + "'");
}
// Whether the object is being created singletonsCurrentlyInCreation, an operation to avoid circular dependencies
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {<!-- -->
this.suppressedExceptions = new LinkedHashSet<>();
}
try {<!-- -->
// Calling the outgoing createBean here returns springbean, and then performs cache-related operations.
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {<!-- -->
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {<!-- -->
throw ex;
}
}
                catch (BeanCreationException ex) {<!-- -->
if (recordSuppressedExceptions) {<!-- -->
for (Exception suppressedException : this.suppressedExceptions) {<!-- -->
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {<!-- -->
if (recordSuppressedExceptions) {<!-- -->
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {<!-- -->
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}

First of all, this putback adds a synchronized lock, which locks the first-level cache object, which means that the instantiation of the object is thread-safe and prevents repeated creation during instantiation. After acquiring the lock, it will obtain from the cache whether there is an instance of the current beanName. If not, beforeSingletonCreation(beanName) will be executed.

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

The core here is the add method of the singletonsCurrentlyInCreation collection, which is the place of early exposure.

private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));

Directly, the passed FunctionalInterface will be called, that is, the createBean method will be executed.

 try {<!-- -->
// Calling the outgoing createBean here returns springbean, and then performs cache-related operations.
singletonObject = singletonFactory.getObject();
newSingleton = true;
}

createBean/doCreateBean

The constructor method is called when doCreateBean is executed.

if (instanceWrapper == null) {<!-- -->
instanceWrapper = createBeanInstance(beanName, mbd, args);
}

Then it is judged that the beanDefinition information is a singleton, circular dependency is allowed (the default is allowed) and the current bean is being created, and the if logic will be entered.

addSingletonFactory, guess from the name whether it is related to our third-level cache.

boolean earlySingletonExposure = (mbd.isSingleton() & amp; & amp; this.allowCircularReferences & amp; & amp;
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));
}

The parameters of the addSingletonFactory method are as follows:

  • beanName, the name of the bean
  • ObjectFactory is a functional interface. When we looked at the getSingleton(name) method before, we also knew that this getObject method will be called to obtain the object and put it into the second-level cache.

If it does not exist in the first-level cache, put the ObjectFactory into the third-level cache, clear the second-level cache and add it to the registered ordered collection.

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {<!-- -->
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {<!-- -->
if (!this.singletonObjects.containsKey(beanName)) {<!-- -->
\t\t\t\t
this.singletonFactories.put(beanName, singletonFactory);
\t\t\t\t
this.earlySingletonObjects.remove(beanName);
\t\t\t\t
this.registeredSingletons.add(beanName);
}
}
}

populateBean

Filling the bean, that is, property injection. Here we will parse the dependencies of the bean, and continue to follow the logic of getBean to complete the instantiation of the current bean.

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {<!-- -->
if (bw == null) {<!-- -->
if (mbd.hasPropertyValues()) {<!-- -->
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {<!-- -->
// property mean property
// Skip property population phase for null instance.
return;
}
}

// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
// Give any InstantiationAwareBeanPostProcessors a chance to modify the bean's state before the properties are populated.
// Postprocessors can be used, for example, in styles that support field injection.
if (!mbd.isSynthetic() & amp; & amp; hasInstantiationAwareBeanPostProcessors()) {<!-- -->
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {<!-- -->
if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {<!-- -->
return;
}
}
}


     PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {<!-- -->
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
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);
}
pvs = newPvs;
}

boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {<!-- -->
if (pvs == null) {<!-- -->
pvs = mbd.getPropertyValues();
}
// Instantiate the aware BeanPostProcessor
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {<!-- -->
// By default, resolvedAutowireMode is 0, so the default is AutoWiredAnnotationBeanPostProcessor for automatic assembly.
  PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {<!-- -->
if (filteredPds == null) {<!-- -->
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
// Go back here to verify the version information of the attribute (BeanNotOfRequiredTypeException)
/**
*Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException:
* Error creating bean with name 'b': Unsatisfied dependency expressed through field 'a';
* nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException:
* Bean named 'a' is expected to be of type 'com.qhyu.cloud.circlarRefrence.A' but was actually of type 'com.sun.proxy.$Proxy34'
*/
pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {<!-- -->
return;
}
}
pvs = pvsToUse;
}
}
if (needsDepCheck) {<!-- -->
if (filteredPds == null) {<!-- -->
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}

if (pvs != null) {<!-- -->
// Spel is parsed at the end of attribute filling.
applyPropertyValues(beanName, mbd, bw, pvs);
}
}

initializeBean

If the bean implements the BeanNameAware, BeanClassLoaderAware or BeanFactoryAware interface, callback invokeAwareMethods(beanName, bean)

Process the init-method defined in the bean, or if the bean implements the InitializingBean interface, call the afterPropertiesSet() method

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {<!-- -->
if (System.getSecurityManager() != null) {<!-- -->
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {<!-- -->
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {<!-- -->
invokeAwareMethods(beanName, bean);
}

Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {<!-- -->
//Execute the before of beanpostProcessor first
// Do something before bean initialization through BeanPostProcessor
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}

try {<!-- -->
//Execute initmethods again
//Call the bean's initialization method to initialize
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()) {<!-- -->
// Then execute the After method of BeanPostProcessor
// Do something after bean initialization through BeanPostProcessor
// entry point of aop
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

return wrappedBean;
}

getSingleton

If there are no problems with the creation, newSingleton = true, continue to execute the addSingleton(beanName, singletonObject) method

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) {<!-- -->
          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 + "'");
          }
          // Whether the object is being created singletonsCurrentlyInCreation, an operation to avoid circular dependencies
          beforeSingletonCreation(beanName);
          boolean newSingleton = false;
          boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
          if (recordSuppressedExceptions) {<!-- -->
             this.suppressedExceptions = new LinkedHashSet<>();
          }
          try {<!-- -->
             // Calling the outgoing createBean here returns springbean, and then performs cache-related operations.
             singletonObject = singletonFactory.getObject();
             newSingleton = true;
          }
          catch (IllegalStateException ex) {<!-- -->
             // Has the singleton object implicitly appeared in the meantime ->
             // if yes, proceed with it since the exception indicates that state.
             singletonObject = this.singletonObjects.get(beanName);
             if (singletonObject == null) {<!-- -->
                throw ex;
             }
          }
          catch (BeanCreationException ex) {<!-- -->
             if (recordSuppressedExceptions) {<!-- -->
                for (Exception suppressedException : this.suppressedExceptions) {<!-- -->
                   ex.addRelatedCause(suppressedException);
                }
             }
             throw ex;
          }
          finally {<!-- -->
             if (recordSuppressedExceptions) {<!-- -->
                this.suppressedExceptions = null;
             }
             afterSingletonCreation(beanName);
          }
          if (newSingleton) {<!-- -->
             addSingleton(beanName, singletonObject);
          }
       }
       return singletonObject;
    }
}

Put the created bean into the first-level cache and remove the first- and second-level cache.

protected void addSingleton(String beanName, Object singletonObject) {<!-- -->
// Thread safe
synchronized (this.singletonObjects) {<!-- -->
//Put it into the first-level cache
this.singletonObjects.put(beanName, singletonObject);
// Remove the third level cache
this.singletonFactories.remove(beanName);
// Remove the second level cache
this.earlySingletonObjects.remove(beanName);
//Put the newly initialized bean into the registered map
this.registeredSingletons.add(beanName);
}
}

Summary

The entire bean initialization process is shown in the figure below. The figure below is the simplest instantiation process of a singleton bean, which does not involve circular dependencies. Circular dependencies and third-level cache will be analyzed in detail in the next chapter. This chapter mainly understands the overall process of bean instantiation, and understands and becomes familiar with the spring working mode.