preface

Earlier, we looked at the source code to see how <bean> in the Spring XML configuration file is parsed and loaded as a BeanDefinition. All BeanDefinitions are stored in the beanFacotry. For example, beanFactory is a factory, BeanDefinition is the drawing of the product, and bean is the factory product. The analysis process is to draw the drawing of the product, and then the factory can start producing products according to the drawing.

Bean creation and initialization

Spring’s initialization of non-lazy-loaded singletons is done when the container is initialized. Let’s take XmlWebApplicationContext as an example, starting with the Refresh () method.

AbstractApplicationContext

The refresh () method call finishBeanFactoryInitialization (the beanFactory) began to singleton object initialization. There’s a little bit of preparation in this step; It also freezes all Beandefinitions to prevent them from being modified during the process. Then call the beanFacotry method to get the factory working.

DefaultListableBeanFactory

Now we’re going to start instantiating, and we see that he’s going to take all beanDefinitionNames and go through them, instantiating singletons that are not lazy-loaded. Factorybeans are also handled here, and those desired to be initialized in factoryBeans are initialized at container initialization time. In all Bean initialization complete, this method can also trigger the callback for SmartInitializingSingleton type of object, this is not elaborate.

GetBean (beanName) is the core code for creating and initializing Bean objects. We will focus on the implementation of this method. This method is a method of the AbstractBeanFactory superclass.

// DefaultListableBeanFactory.java
@Override
public void preInstantiateSingletons(a) throws BeansException {
    if (logger.isTraceEnabled()) {
        logger.trace("Pre-instantiating singletons in " + this);
    }
    
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        // It is not an abstract class and is a singleton and not lazily loaded
        if(! bd.isAbstract() && bd.isSingleton() && ! bd.isLazyInit()) {if (isFactoryBean(beanName)) {
                Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (bean instanceofFactoryBean) { FactoryBean<? > factory = (FactoryBean<? >) bean;boolean isEagerInit;
                    if(System.getSecurityManager() ! =null && factory instanceofSmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<? >) factory)::isEagerInit, getAccessControlContext()); }else {
                        isEagerInit = (factory instanceofSmartFactoryBean && ((SmartFactoryBean<? >) factory).isEagerInit()); }if(isEagerInit) { getBean(beanName); }}}else{ getBean(beanName); }}}for (String beanName : beanNames) {
        Object singletonInstance = getSingleton(beanName);
        if (singletonInstance instanceof SmartInitializingSingleton) {
            SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
            if(System.getSecurityManager() ! =null) {
                AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                        smartSingleton.afterSingletonsInstantiated();
                        return null;
                }, getAccessControlContext());
            }
            else{ smartSingleton.afterSingletonsInstantiated(); }}}}Copy the code

AbstractBeanFactory

The getBean(beanName) method in turn calls the doGetBean() method. In Spring, methods that start with do usually do the real work. This is a long method, 150 or 60 lines. So let’s go through the logic of this

  • BeanName processing
  • First try to fetch the bean instance from the cache and return if there is one
  • If the current beanFacotry does not find a BeanDefinition for the beanName, try to get the bean from the parent container
  • Adding a beanName to the alreadyCreated collection indicates that the beanName has been created or will be created soon
  • The depends-on attribute of the bean is processed, and the Depends -on class is created and initialized
  • Create the initialization bean and return the bean

This logic does not specify how to create a singleton bean, just create an initialization bean. Now let’s look at the getSingleton(beanName, singletonFactory) method and see what the logic is.

// AbstractBeanFactory.java
@Override
public Object getBean(String name) throws BeansException {
        return doGetBean(name, null.null.false);
}

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

    String beanName = transformedBeanName(name);
    Object bean;

    // Try to fetch the bean from the level 3 cache
    Object sharedInstance = getSingleton(beanName);
    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 + "'");
            }
        }
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {
        if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
        }

        BeanFactory parentBeanFactory = getParentBeanFactory();
        // If the current beanFacotry does not find the BeanDefinition of beanName, try to get it from the parent container
        if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {
            String nameToLookup = originalBeanName(name);
            if (parentBeanFactory instanceof AbstractBeanFactory) {
                return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                                nameToLookup, requiredType, args, typeCheckOnly);
            }
            else if(args ! =null) {
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            else if(requiredType ! =null) {
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
            else {
                return(T) parentBeanFactory.getBean(nameToLookup); }}if(! typeCheckOnly) { markBeanAsCreated(beanName); }try {
            RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            checkMergedBeanDefinition(mbd, beanName, args);

            // Handle the depends-on attribute, create and initialize the depends-on attribute
            String[] dependsOn = mbd.getDependsOn();
            if(dependsOn ! =null) {
                for (String dep : dependsOn) {
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                            "Circular depends-on relationship 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); }}}// Create a singleton
            if (mbd.isSingleton()) {
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        return createBean(beanName, mbd, args);
                    }
                    catch (BeansException ex) {
                        destroySingleton(beanName);
                        throwex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }// Create a prototype object
            else if (mbd.isPrototype()) {
                Object prototypeInstance = null;
                try {
                    beforePrototypeCreation(beanName);
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    afterPrototypeCreation(beanName);
                }
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }

            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, () -> {
                        beforePrototypeCreation(beanName);
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        finally{ afterPrototypeCreation(beanName); }}); 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); }}}catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throwex; }}// The getBean is passed in as null
    if(requiredType ! =null && !requiredType.isInstance(bean)) {
        try {
            T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
            if (convertedBean == null) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
            return convertedBean;
        }
        catch (TypeMismatchException ex) {
            if (logger.isTraceEnabled()) {
                logger.trace("Failed to convert bean '" + name + "' to required type '" +
                                    ClassUtils.getQualifiedName(requiredType) + "'", ex);
            }
            throw newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); }}return (T) bean;
}

Copy the code

DefaultSingletonBeanRegistry

The input arguments to this method are beanName and an ObjectFactory. Let’s look at the logic of this approach first

  • Try to fetch from level 1 cache singletonObjects, return if present, create if not present
  • Before to create, add beanName singletonsCurrentlyInCreation collection, said the bean in creating
  • Call ObjectFactory to get a singleton instance
  • Singleton bean has been created, can the beanName from singletonsCurrentlyInCreation removed
  • The newly created singleton object is placed in the cache
    • Put in level 1 cache
    • Deleted from level 2 cache and level 3 cache
// DefaultSingletonBeanRegistry.java
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 not available from singletonObjects, start creation
        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 + "'");
            }
            / / this step will add beanName singletonsCurrentlyInCreation collection, said this bean in creation
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // Call ObjectFactory to get the object
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            catch (IllegalStateException ex) {
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    throwex; }}catch (BeanCreationException ex) {
                if (recordSuppressedExceptions) {
                    for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); }}throw ex;
            }
            finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                / / to the singleton bean has been created, the beanName from singletonsCurrentlyInCreation removed
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                // The newly created singleton is placed in the cacheaddSingleton(beanName, singletonObject); }}returnsingletonObject; }}protected void beforeSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
        throw newBeanCurrentlyInCreationException(beanName); }}protected void afterSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
        throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation"); }}// This method is crucial. The effect is to put the singleton into the cache
protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        // Put it into level 1 cache
        this.singletonObjects.put(beanName, singletonObject);
        // Delete the level 3 cache
        this.singletonFactories.remove(beanName);
        // Fade out the level 2 cache
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName); }}Copy the code

AbstractAutowireCapableBeanFactory

Next we see singletonFactory emphatically. GetObject (). The ObjectFactory singletonFactory
type into the parameter, when AbstractApplicationContext call this getSingleton method, the incoming is an anonymous object, only one createBean () method. The realization of this method in AbstractAutowireCapableBeanFactory class, DefaultListableBeanFactory parent class.

This method shows that there is some preparatory work, and the real work is in doCreateBean(). But there is a small bit of attention, this method by calling resolveBeforeInstantiation () method, to rear a processor can return a proxy object. If the returned proxy object is not empty, it is returned and replaced with the original bean.

// AbstractAutowireCapableBeanFactory.java
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
                throws BeanCreationException {

    if (logger.isTraceEnabled()) {
        logger.trace("Creating instance of bean '" + beanName + "'"); } RootBeanDefinition mbdToUse = mbd; Class<? > resolvedClass = resolveBeanClass(mbd, beanName);if(resolvedClass ! =null&&! mbd.hasBeanClass() && mbd.getBeanClassName() ! =null) {
        mbdToUse = new RootBeanDefinition(mbd);
        mbdToUse.setBeanClass(resolvedClass);
    }

    // Prepare method overrides.
    try {
        mbdToUse.prepareMethodOverrides();
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
                        beanName, "Validation of method overrides failed", ex);
    }

    try {
        / / here will call InstantiationAwareBeanPostProcessor post processor
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if(bean ! =null) {
            returnbean; }}catch (Throwable ex) {
        throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                        "BeanPostProcessor before instantiation of bean failed", ex);
    }

    try {
        // Start creating
        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(
                        mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex); }}Copy the code

Take a look at doCreateBean() for real work

  • The bean is created with the createBeanInstance() method and wrapped in a BeanWrapper
    • If any Supplier exists, obtain the bean through Supplier
    • If there is a factory-method, get the bean from the configured factory-method
    • Parsing constructor, constructor input parameter
    • Select the constructor to create the object through reflection
    • Return to wrapper class
  • Modified BeanDefinition MergedBeanDefinitionPostProcessor post processor
  • Determines whether the beans being created are exposed ahead of time through the three-level cache ObjectFactory
    • By the parent classaddSingletonFactory()Method is placed in and removed from the level 3 cache
  • Property injection and initialization of the created bean
  • Check that the bean is the same object as the one injected by another bean in the second-level cache
  • Register beans that require custom actions at destruction time
// AbstractAutowireCapableBeanFactory.java
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
                throws BeanCreationException {

    // Instantiate the bean wrapped in BeanWrapper
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } Object bean = instanceWrapper.getWrappedInstance(); Class<? > beanType = instanceWrapper.getWrappedClass();if(beanType ! = NullBean.class) { mbd.resolvedTargetType = beanType; }/ / rear MergedBeanDefinitionPostProcessor modified BeanDefinition processor
    synchronized (mbd.postProcessingLock) {
        if(! mbd.postProcessed) {try {
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Post-processing of merged bean definition failed", ex);
            }
            mbd.postProcessed = true; }}// singleton and allow cyclic references and in creation => Pre-expose the bean being created through the level 3 cache ObjectFactory
    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));
    }

    // Perform property injection and initialization on the created bean
    Object exposedObject = bean;
    try {
        populateBean(beanName, mbd, instanceWrapper);
        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); }}InitializeBean () is the same as the object before initializeBean() was initialized.
    // If it is a different object, there is a problem with throwing an exception if the value of the property injected by another bean is not the same as that of the actual final bean
    if (earlySingletonExposure) {
        Object earlySingletonReference = getSingleton(beanName, false);
        if(earlySingletonReference ! =null) {
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            }
            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                String[] dependentBeans = getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                for (String dependentBean : dependentBeans) {
                    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                        actualDependentBeans.add(dependentBean);
                    }
                }
                if(! actualDependentBeans.isEmpty()) {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 " +
                                    "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example."); }}}}// Register beans that require custom actions when destroyed
    try {
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }

    return exposedObject;
}

Copy the code

Take a look at how to instantiate and create a wrapper object. The main logic is

  • Parsing Class objects
  • Try using Supplier, factory-method
  • Inferential construction method
  • Instantiated and wrapped as a BeanWrapper using reflection
// AbstractAutowireCapableBeanFactory.java
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { 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());
    }
    
    // If any Supplier exists, obtain the bean from SupplierSupplier<? > instanceSupplier = mbd.getInstanceSupplier();if(instanceSupplier ! =null) {
        return obtainFromSupplier(instanceSupplier, beanName);
    }

    // If there is a factory-method, get the bean from the configured factory-method
    if(mbd.getFactoryMethodName() ! =null) {
        return instantiateUsingFactoryMethod(beanName, mbd, args);
    }

    // Whether the bean's constructor and constructor parameters have been parsed and can be used directly
    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 {
            returninstantiateBean(beanName, mbd); }}// This section is about constructors, constructor parameters parsing, processingConstructor<? >[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);if(ctors ! =null|| mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || ! ObjectUtils.isEmpty(args)) {return autowireConstructor(beanName, mbd, ctors, args);
    }
    
    ctors = mbd.getPreferredConstructors();
    if(ctors ! =null) {
        return autowireConstructor(beanName, mbd, ctors, null);
    }

    // Use the no-argument constructor
    return instantiateBean(beanName, mbd);
}
Copy the code

The factory class is placed in the level 3 cache if the current bean(a singleton in creation that allows loop dependency) is judged to have been exposed early.

/ / DefaultSingletonBeanRegistry. Java, is AbstractAutowireCapableBeanFactory parent class
// Allow loop dependent singletons to be exposed early
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);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName); }}}Copy the code

Then look at the property injection and initialization after the object is created

// AbstractAutowireCapableBeanFactory.java
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 {
            return; }}/ / to InstantiationAwareBeanPostProcessor a chance before attributes into the bean can be modified
    if(! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                if(! ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {return;
                }
            }
        }
    }

    PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
    // Inject according to the dependency injection mode of the Bean configuration. Default is 0, do not follow the following logic
    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;
    }

    // Whether there is a post-processor to modify attribute values
    boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
    // Whether dependencies need to be checked. Not required by default
    booleanneedsDepCheck = (mbd.getDependencyCheck() ! = AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds =null;
    // Give the post-processor a chance to modify the property value before it is injected
    if (hasInstAwareBpps) {
        if (pvs == null) {
            pvs = mbd.getPropertyValues();
        }
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                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; }}}if (needsDepCheck) {
        if (filteredPds == null) {
            filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
        }
        // Check dependencies
        checkDependencies(beanName, mbd, filteredPds, pvs);
    }

    if(pvs ! =null) {
        // Inject the property value into the beanapplyPropertyValues(beanName, mbd, bw, pvs); }}Copy the code

Rear processor

During the entire Bean loading process,

1. Before Bean instantiation

Before the bean instantiation, allowing InstantiationAwareBeanPostProcessor through postProcessBeforeInstantiation () method to create a proxy class, completely replace the target bean. If the agent class objects to create success, can call all the post processor postProcessAfterInitialization () method, to deal with the proxy class object. It then returns directly, ending the Bean’s creation initialization.

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

   @Nullable
   default Object postProcessBeforeInstantiation(Class
        beanClass, String beanName) throws BeansException {
   	return null;
   }
       
   default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
   	return true;
   }
       
   @Nullable
   default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
   	return null;
   }
       
   @Deprecated
   @Nullable
   default PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
   	returnpvs; }}Copy the code

2. Infer structural precautions

Here will use call interface SmartInstantiationAwareBeanPostProcessor determineCandidateConstructors () method, which returns a constructor array. Pay attention to the SmartInstantiationAwareBeanPostProcessor interface is inherited from InstantiationAwareBeanPostProcessor here.

public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {

    @Nullable
    defaultClass<? > predictBeanType(Class<? > beanClass, String beanName)throws BeansException {
        return null;
    }

    @Nullable
    defaultConstructor<? >[] determineCandidateConstructors(Class<? > beanClass, String beanName)throws BeansException {
        return null;
    }

    default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        returnbean; }}Copy the code

3. MergedBeanDefinition processing

Call MergedBeanDefinitionPostProcessor postProcessMergedBeanDefinition () method. Here are a few common implementation class, CommonAnnotationBeanPostProcessor, AutowiredAnnotationBeanPostProcessor, Field annotations such as @postconstruct, @Resource, and @Autowire are scanned and stored in BeanDefinition for subsequent dependency injection. In the form of an XML configuration bean, this step is actually quite limited.

public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {

    void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class
        beanType, String beanName);

    default void resetBeanDefinition(String beanName) {}}Copy the code

4. The factory class in the level 3 cache exposes beans ahead of time

The actual execution time should be invoked when attempting to fetch the bean from the third-level cache singletonFactories. From the three levels of the cache is a ObjectFactory, it will pass the SmartInstantiationAwareBeanPostProcessor getEarlyBeanReference () method to get ahead of exposed completely bean uninitialized.

SmartInstantiationAwareBeanPostProcessor source is from above, the default bean was exposed in advance, no additional operation.

5. Customize attribute padding before attribute padding

When bean ready to fill the Spring allows us through InstantiationAwareBeanPostProcessor postProcessAfterInstantiation () method, a custom attribute injection. The return type of this method is Boolean. When false is returned, the property population is terminated without following the Spring property population logic.

6. Modify the attribute value

After the property value is retrieved, there is one more chance to modify the property value before injection. InstantiationAwareBeanPostProcessor provides postProcessProperties () method.

7. Apply all BeanPostProcessors before initialization

At this point, the bean properties have been injected, the Aware interface has been processed, and the initialization operation is performed. In this step, all the post processor postProcessBeforeInitialization () method will be invoked

8. Apply all BeanPostProcessors after initialization

All of the bean’s initialization methods have been executed, and the entire bean is actually created. At that time all the post processor postProcessAfterInitialization () is called

9. Registered destruction method

All operations to the bean are almost complete, and the destruction method is registered. Simple DestructionAwareBeanPostProcessor the source of a look.

public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {

    void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;

    default boolean requiresDestruction(Object bean) {
            return true; }}Copy the code

Circular dependencies

Some argue that if you have cyclic dependencies in your code, then the code is poorly designed. But Spring doesn’t completely block this path, it does allow circular dependencies.

Exposure in advance

For example, there are OneComponent and TwoComponent classes that depend on each other.

@Component
public class OneComponent {
    @Autowired
    private TwoComponent twoComponent;
}

@Component
public class TwoComponent {
    @Autowired
    private OneComponent oneComponent;
}

Copy the code

Let’s consider the core problem with loop dependencies: when I do my TwoComponent property injection, I can’t get your OneComponent bead reference. Well, as long as you can get a quote in advance, the problem is solved. Then I can use a special cache (singletonObjects) to store the created bean; Add an earlySingletonObjects cache for uncreated beans and pre-expose them.

Three levels of cache

If you look at the flow chart above, there seems to be nothing wrong with it. Spring, however, uses a three-level cache to address cyclic dependencies

  • SingletonObjects: A level 1 cache that holds complete beans
  • EarlySingletonObjects: A second-level cache that holds beans that are not yet complete
  • SingletonFactories: A three-level cache that stores the ObjectFactory of the bean being produced

Why do I need to design a singletonFactories cache? In fact, the three-level cache is designed primarily to match Spring’s AOP features.

For normal singleton, AOP is after initialization method, and application of AbstractAutoProxyCreator postProcessAfterInitialization () method. However, for singletons that need to be exposed ahead of time because of loop dependencies, a two-quarter cache is problematic.

We know that property injection precedes method initialization, so of course it precedes AOP. With only a two-level cache, I TwoComponent do the property injection, and what I pull from the two-level cache is the bean that OneComponent precedes AOP. With all the steps completed, TwoComponent’s bean goes to level 1 cache, then TwoComponent’s bean starts property injection, initializes method calls, the AOP returns proxy objects, and finally the bean goes to level 1 cache. Notice, here’s the problem. The TwoComponent properties inject OneComponent’s original singleton bean, whereas the actual singleton corresponding to OneComponent is an AOP-aop proxy object bean, which is not the same thing at all.

So to solve this problem, the singletonFactories cache was introduced. The cache memory is a singleton corresponding ObjectFactory object, the ObjectFactory object will invoke SmartInstantiationAwareBeanPostProcessor implementation class of getEarlyBeanReference () method, To generate a pre-exposure reference.

Singleton objects that do not require AOP will return the bean itself directly in this step; An AOP singleton is required, and in this step the proxy object is generated in advance by calling the AbstractautoxyCreator getEarlyBeanReference() method. This avoids the problem of proxy objects after AOP being different from the original singleton.

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
		implements SmartInstantiationAwareBeanPostProcessor.BeanFactoryAware {
    / / to omit...
    
    // AOP singleton calls are required to generate proxy objects ahead of time for early exposure
    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            // Record pre-proxied beans
            this.earlyProxyReferences.add(cacheKey);
        }
        return wrapIfNecessary(bean, beanName, cacheKey);
    }

    / / to omit...
    
    @Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
        if(bean ! =null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            // Determine whether to advance proxy
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                // The proxy class is created if the current object needs it
                returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
    }
    
    / / to omit...
}
Copy the code

To tidy up the loop dependent process, again using OneComponent and TwoComponent as examples

  • Try to get the bean corresponding to OneComponent from the cache
  • If no, create an OneComponent instance
  • Pre-exposure to level 3 cache
  • Property injection is performed on the created bean
    • Try to get the bean for the TwoComponent from the cache
    • If there is none in the cache, start creating the TwoComponent instance
    • Pre-exposure to level 3 cache
    • Inject properties into the TwoComponent instance
    • The pre-exposed OneComponent instance (or proxy object in the case of AOP) is fetched from the level 3 cache and injected into the level 2 cache
    • Initialize the method call
    • Apply AOP(if available)
    • The TwoComponent beans are complete and placed in the level 1 cache
  • Get the TwoComponent bean from the level 1 cache and inject it
  • Initialize the method call
  • Apply AOP(if available)
  • All beans corresponding to OneComponent are completed and put into level 1 cache
  • The operation on the two beans that the loop depends on completes

Loop-dependent exception raised by @async

By the way, I’m going to mention an issue I encountered earlier, @async loop-dependent exceptions. This one also has proxies, but why can’t level 3 cache solve this? Briefly, the key is the getEarlyBeanReference() method. @ Async is handled by AsyncAnnotationBeanPostProcessor, not like dealing with AOP AbstractAutoProxyCreator, Implement interface SmartInstantiationAwareBeanPostProcessor getEarlyBeanReference () method, lead to can’t expose proxy objects in advance, cause the injection and the final object, throw an exception.

conclusion

This article is more complicated and difficult. I have time to rearrange it later. Take a look at the following figure to review the overall process of Bean creation and initialization.