Spring Bean creation process

● study the spring bean creation process through annotation. The entry is the AnnotationConfigApplicationContext class.
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);

● look at the three steps performed in this construction method:
○ call this() method:
■ instantiate the ioc container DefaultListableBeanFactory;
■ complete the instantiation of AnnotationBeanDefinitionReader bean parser;
■ complete the instantiation of ClassPathBeanDefinitionScanner (I don't know what this is for)
○ call register(componentClasses) method:
■ resolve the currently passed in configuration into beanDefinition and register it in the container.
○ call refresh() method (Spring core method)
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
//Calling the parameterless constructor will first call the parameterless constructor of the parent class GenericApplicationContext, which is mainly used to instantiate the BeanFactory factory;
//Then create AnnotatedBeanDefinitionReader annotation Bean parser
this();
//Resolve the current configuration class into beanDefinition and register it in the container.
register(componentClasses);
//Core method, refresh container
refresh();
}

● mainly focus on the refresh() method, which is the core method of SpringIOC startup process and Bean creation.
○ invoke invokebeanfactoryprocessors (beanfactory) method: the function is to resolve all beans into beanDefinition
○ call the finishBeanFactoryInitialization(beanFactory) method: complete the instantiation of all non lazy loaded and singleton beans.

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        //... Omit some unimportant methods (which will be described in detail later when Spring starts the process)

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Resolve all beans into BeanDefinition
            invokeBeanFactoryPostProcessors(beanFactory);

           //... Omitted

            // Instantiate all non lazy loaded beans (spring's core method of creating beans is also the core method of Bean life cycle)
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }
        //... Omit some unimportant methods
    }
}

● the core method of Bean creation is finishBeanFactoryInitialization(). Let's see what's actually achieved inside.
○ beanfactory is called Preinstantiatesingletons() method: it is mainly used to instantiate all non lazy loaded and singleton beans.
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
//... omit some unimportant methods

// Instantiate all non lazy loaded singleton beans 
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();

}

● beanFactory. The main logic in preinstantiatesingletons() is as follows:
○ obtain the beanName of all singleton beans in the current ioc container;
○ traverse all beannames and obtain the merged BeanDefinition object corresponding to each beanName;
○ if it is a FactoryBean object, directly follow the instantiation logic of FactoryBean; otherwise, directly follow the instantiation logic of bean, that is, directly call the getBean(beanName) method.
○ initialize logic after calling all bean s.
@Override
public void preInstantiateSingletons() throws BeansException {

// ...
// Gets the beanName of all beans in the container
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

// Trigger the initialization of all non lazy loaded singleton beans
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
    //Get a merged BeanDefinition
    RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
    if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
        //Determine whether it is a FactoryBean object
        if (isFactoryBean(beanName)) {
            //If it is a FactoryBean object, obtain the FactoryBean through & + beanname
            Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
            if (bean instanceof FactoryBean) {
                FactoryBean<?> factory = (FactoryBean<?>) bean;
                boolean isEagerInit;
                if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                    isEagerInit = AccessController.doPrivileged(
                        (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
                        getAccessControlContext());
                }
                else {
                    isEagerInit = (factory instanceof SmartFactoryBean &&
                                   ((SmartFactoryBean<?>) factory).isEagerInit());
                }
                if (isEagerInit) {
                    getBean(beanName);
                }
            }
        }
        else {
            //Call the initialize Bean method
            getBean(beanName);
        }
    }
}
// Initialize callback after calling all beans
// Trigger post-initialization callback for all applicable beans...
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();
        }
    }
}

}

● the getBean() method will continue to call the doGetBean() method. The main logic is as follows:
○ call the getSingleton(beanName) method to get the bean from the cache first. If there is a current bean in the cache, the bean will be returned directly. Otherwise, proceed to the next step. How does Spring solve the problem of circular dependency?

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // First get from singletonObjects (L1 cache)
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // Get from earlySingletonObjects (L2 cache)
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && 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) {
                        // Finally, get from singletonFactories (L3 cache)
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            // 
                            singletonObject = singletonFactory.getObject();
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}

○ via string [] dependson = MBD Getdependson() determines whether the current bean has dependencies. If there are dependencies, instantiate them first.

// Instantiate all dependencies of the current Bean
// Guarantee initialization of beans that the current bean depends on.
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);
        }
    }
}

○ call the createBean(beanName, mbd, args) method to start instantiating the bean.

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

    String beanName = transformedBeanName(name);
    Object bean;

    // Get the Bean from the cache first. If you get a direct return, you can't get it. Start instantiating a Bean and then put it into the cache.
    // Here is the main way to implement the singleton of Spring Bean (implement the singleton of Bean through Map 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 + "'");
            }
        }
        // The function of this method is to judge whether the returned object is the FactoryBean native object or the object returned by the getObject() method according to the BeanName. If it starts with &, it is the FactoryBean native object, otherwise it is the object returned by getObject().
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {
       // ... Omit unimportant logic
        
        try {
            RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            checkMergedBeanDefinition(mbd, beanName, args);

            // Instantiate all dependencies of the current Bean
            // Guarantee initialization of beans that the current bean depends on.
            String[] dependsOn = mbd.getDependsOn();
            // ...

			// Instantiate a singleton Bean
            // Create bean instance.
            if (mbd.isSingleton()) {
                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;
                    }
                });
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
            // Handle instantiation of other Bean actions
        }
        catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }
	// Omit unimportant logic
    return (T) bean;
}

Finally, call AbstractAutowireCapableBeanFactory.. The logic of the docreatebean() method is as follows:
○ execute the postprocessor method postprocessbeforeinstance (beanclass, beanname) of InstantiationAwareBeanPostProcessor. (the core function of Aop is completed through this post processor. The annotation awareaspectjautoproxycreator. Postprocessbeforeinstance () method, and the core logic will be described later). How does spring Aop work?
○ call the doCreateBean(beanName, mbdToUse, args) method to complete bean instantiation.
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
//... omit some unimportant methods
try {
//Execute instantiaawarebeanpostprocessor to initialize the postprocessor method. Here is the main post processor that handles AOP.
//The core post processor of AOP, AnnotationAwareAspectJAutoProxyCreator, implements the instantiawarebeanpostprocessor interface. The implementation process of AOP will be introduced later.

    // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
            return bean;
        }
    }
    catch (Throwable ex) {
        throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                                        "BeanPostProcessor before instantiation of bean failed", ex);
    }

    try {
        //Start instantiating Bean
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        if (logger.isTraceEnabled()) {
            logger.trace("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
    }
    catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
        // A previously detected exception with proper bean creation context already,
        // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
        throw ex;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
            mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
    }
}

● the main implementation logic of doCreateBean() method, the core method of instantiating beans, is as follows:
○ call the createBeanInstance() method to instantiate the bean. Spring instantiation constructor inference principle?
■ infer the constructor used for instantiation through determineconstructors frombeanpostprocessors (bean class, bean name). Here, only the AutowiredAnnotationBeanPostProcessor post processor implements this method. The main contents of this method are summarized as follows:
● first, we need to know that if a bean has multiple constructors, we can tell the container through the @ Autowired annotation which constructor we need to instantiate.
● if there are more than two constructors marked with @ Autowired, the IOC container will report an error.
Finally, calling a @Autowired annotation is required=false, then it will be added to the candidate list. If there is no reference constructor at this time, it will also be added to the candidate list. If there is only one @AutoWired, this structure will be returned.
● if there are more than 1 construction methods, but none of them have @ AutoWired, null will be returned.
Finally, call ContructorResolver.. The instantiate () method instantiates beans through the reflection mechanism. Note that the instantiated bean is a semi-finished product, and attribute injection has not been performed yet.
○ call applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName) to execute the post processor of MergedBeanDefinitionPostProcessor. This post processor is mainly used for the pre work of bean attribute injection, and put the attributes and methods with @ Autowired, @ Resource and other annotations in all classes into RejectionMetadata for automatic assembly later@ Implementation principle of dependency injection annotations such as Autowired and @ Inject?
○ call the addsingletonfactory (beanname, () - > getearlybeanreference (beanname, MBD, bean)) method.
■ this is through the L3 cache singletonFactories. Put (bean name, singleton factory) to solve the circular dependency problem.
○ call the populateBean() method to complete the bean property filling.
■ this is mainly through autowiredannotationbeanpostprocessor The postprocessproperties method performs property injection. The main logic is to get the InjectionMetadata, and then traverse each attribute for attribute injection.
○ call the initializeBean() method to initialize the bean.
■ call the invokeAwareMethods(beanName, bean) callback method to execute setXXX() methods such as BeanNameAware, BeanFactoryAware, BeanClassLoaderAware, etc.
■ call the post processor pre method of BeanPostProcessor through the applybeanpoprocessors before initialization (wrapped bean, beanname) method
■ call the bean initialization method through the invokeinitialmethods (beanname, wrapped bean, MBD) method;
● first call the afterpropertieset() method that implements the InitializingBean interface;
● call the method in the custom init method.
■ call the BeanPostProcessor postprocessor method through the applybeanpoprocessorsafterinitialization (wrapped bean, beanname) method.

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

    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        // Create Bean instance
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();
    if (beanType != NullBean.class) {
        mbd.resolvedTargetType = beanType;
    }

    // Allow post-processors to modify the merged bean definition.
    synchronized (mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            try {
                // Call the MergedBeanDefinitionPostProcessor post processor to complete the automatic assembly function of the AutoWiredAnnotationBeanPostProcessor post processor.
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                                "Post-processing of merged bean definition failed", ex);
            }
            mbd.postProcessed = true;
        }
    }

    // Eagerly cache singletons to be able to resolve circular references
    // even when triggered by lifecycle interfaces like BeanFactoryAware.
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                                      isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        if (logger.isTraceEnabled()) {
            logger.trace("Eagerly caching bean '" + beanName +
                         "' to allow for resolving potential circular references");
        }
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
        // Attribute filling
        populateBean(beanName, mbd, instanceWrapper);
        // Initialize Bean
        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);
        }
    }
    
    // ... Omit some unimportant methods

    // Register bean as disposable.
    try {
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }

    return exposedObject;
}

Keywords: Java Spring Back-end

Added by munuindra on Mon, 10 Jan 2022 07:33:00 +0200