Can't you remember the life cycle of Spring Bean? Read this article and you will know the method!

Posted by The_Black_Knight on Sat, 16 Oct 2021 21:06:28 +0200

1, Foreword

Last time we were Teach you how to solve circular dependency step by step, and explore the mystery of L3 cache step by step The way Spring solves circular dependency is analyzed in. In Section 6, doCreateBean (AbstractAutowireCapableBeanFactory class) has vaguely seen several stages of the Bean's life cycle.

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {
 
        //Instantiate bean
        BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
 
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            //When added to the L3 cache, getEarlyBeanReference will return the singleton factory
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }
 
        Object exposedObject = bean;
        //Attribute assignment
        populateBean(beanName, mbd, instanceWrapper);
        //initialization
        exposedObject = initializeBean(beanName, exposedObject, mbd);
 
        if (earlySingletonExposure) {
            //Find from L2 cache
            Object earlySingletonReference = getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                //Return the bean in the L2 cache, where it may be the object after proxy
                exposedObject = earlySingletonReference;
 
            }
        }
        //Prepare before destruction and register the beans to be destroyed
		registerDisposableBeanIfNecessary(beanName, bean, mbd);

        return exposedObject;
    }

doCreateBean basically describes the general framework of the Bean life cycle - instantiation, attribute assignment and initialization. Of course, the last step of the Bean is destruction.

Basic phase diagram

Please remember this picture. Maybe you won't know it later.

2, Instantiation and attribute assignment phase

When the container is started, it will scan the specified package according to the configuration or annotation, convert the classes in it into BeanDefinition, and concentrate them in the beanDefinitionMap variable of DefaultListableBeanFactory class.

Beandfinition in Spring can be used to describe the scope of a bean, whether it is lazy to load, whether it is a singleton, etc.

After the container is started, when we try to get a Bean from the container, the first step is to instantiate the Bean. To put it bluntly, you need to call the constructor.

Note that the resolveBeforeInstantiation method is invoked before calling doCreateBean.

1,resolveBeforeInstantiation

    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {
        //Omit some codes
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
            return bean;
        }
        //Officially enter the life cycle
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);

        return beanInstance;
    }

	protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
		Object bean = null;
		if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
			if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
				Class<?> targetType = determineTargetType(beanName, mbd);
				if (targetType != null) {
                    //Find the instantiaawarebeanpostprocessor and execute the corresponding method
					bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);          
                    //When the return is not null, find the BeanPostProcessor and execute the corresponding method
					if (bean != null) {
						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
					}
				}
			}
			mbd.beforeInstantiationResolved = (bean != null);
		}
		return bean;
	}

When resolvebeforeinstance returns a non null bean, createBean will return directly. In other words, the subsequent instantiation, attribute assignment and initialization phases will not be carried out. This step will give the BeanPostProcessor a chance to return the proxy instead of the current bean.

The core method of resolvebeforeinstance is as follows:

	protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
        //Get the implementation classes of all beanpostprocessors
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
            //If it is of instantiaawarebeanpostprocessor type, execute the postprocessbeforeinstance method
			if (bp instanceof InstantiationAwareBeanPostProcessor) {
				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
				Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
				if (result != null) {
					return result;
				}
			}
		}
		return null;
	}
    
	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {
		Object result = existingBean;
        //Get the implementation classes of all beanpostprocessors
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
            //Execute the postProcessAfterInitialization method one by one
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}

2,BeanPostProcessor

BeanPostProcessor is an extension interface provided to us by the container, also known as the post processor of Bean. We can implement this interface to add business specific logic.

The BeanPostProcessor implementation class is also an ordinary Bean. How does Spring ensure that the BeanPostProcessor implementation class is loaded before the Bean written by the developer?

This brings us back to the most familiar refresh method

    public void refresh() {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);


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

        // Invoke factory processors registered as beans in the context.
        invokeBeanFactoryPostProcessors(beanFactory);

        // Register bean processors that intercept bean creation.
        registerBeanPostProcessors(beanFactory);

        // Initialize message source for this context.
        initMessageSource();

        // Initialize event multicaster for this context.
        initApplicationEventMulticaster();

        // Initialize other special beans in specific context subclasses.
        onRefresh();

        // Check for listener beans and register them.
        registerListeners();

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

        // Last step: publish corresponding event.
        finishRefresh();
    }

The registerBeanPostProcessors method will register the BeanPostProcessor and complete the loading of non lazy loaded singleton beans in finishBeanFactoryInitialization.

Thus, the BeanPostProcessor will be loaded before the business Bean, so it can be called before the business Bean is instantiated.

3,instantiateBean

Now you can enter doCreateBean. createBeanInstance contains the logic of instantiating beans and encapsulating them as beanwrappers, and internally provides a variety of instantiation methods.

    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
        //Convert bean to class object
        Class<?> beanClass = resolveBeanClass(mbd, beanName);

        //Based on Supplier instantiation
        Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
        if (instanceSupplier != null) {
            return obtainFromSupplier(instanceSupplier, beanName);
        }

        //Factory based method instantiation
        if (mbd.getFactoryMethodName() != null) {
            return instantiateUsingFactoryMethod(beanName, mbd, args);
        }

        //Omit some codes

        //Instantiation based on parameterized constructor
        Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
        if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
                mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
            return autowireConstructor(beanName, mbd, ctors, args);
        }
           
        //Instantiation based on parameterless constructor
        return instantiateBean(beanName, mbd);
    }

Interested students can go deep into the interior of these methods, which will not be discussed here.

4,populateBean

After instantiation, it will enter the attribute assignment phase.

	protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
		boolean continueWithPropertyPopulation = true;

		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
                //Postprocessafterinstance is called if it is of InstantiationAwareBeanPostProcessor type
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
						continueWithPropertyPopulation = false;
						break;
					}
				}
			}
		}

        //Once the postprocessafterinstance of any instantiaawarebeanpostprocessor returns false, the property injection phase will no longer be carried out and directly enter the next phase
		if (!continueWithPropertyPopulation) {
			return;
		}

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

		if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
			//Inject by name
			if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			//Injection by type
			if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			pvs = newPvs;
		}

        //Omit the other two extension points of instantiaawarebeanpostprocessor and the following dependency checks

		if (pvs != null) {
            //Attribute injection
			applyPropertyValues(beanName, mbd, bw, pvs);
		}
	}

Here, the instantiation and attribute assignment phase ends. Let's sort out the extension points

  3, Initialization phase

Start with initializeBean in doCreateBean

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

        //2. Call the preprocessing of BeanPostProcessor
		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}
        
        //3.1 calling the afterpropertieset method of InitializingBean
        //3.2 calling custom init method
		try {
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
        
        //4. Call post-processing of BeanPostProcessor
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

1. Set Aware related dependencies

	private void invokeAwareMethods(final String beanName, final Object bean) {
		if (bean instanceof Aware) {
            //If the bean implements the BeanNameAware interface, set the BeanName
			if (bean instanceof BeanNameAware) {
				((BeanNameAware) bean).setBeanName(beanName);
			}
            //If the bean implements the BeanClassLoaderAware interface, set BeanClassLoader
			if (bean instanceof BeanClassLoaderAware) {
				ClassLoader bcl = getBeanClassLoader();
				if (bcl != null) {
					((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
				}
			}
            //If the bean implements the BeanFactory aware interface, set BeanFactory
			if (bean instanceof BeanFactoryAware) {
				((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
			}
		}
	}

It can be seen from here that as long as the Bean implements the specified Aware interface, Spring will inject these Aware related information into the Bean.

2. Call preprocessing of BeanPostProcessor

	public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
        //Get the implementation classes of all beanpostprocessors
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
            //Call the postProcessBeforeInitialization method one by one
			Object current = processor.postProcessBeforeInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}

This step will get the implementation classes of all BeanPostProcessor in the container and call the postProcessBeforeInitialization method one by one. If its return is not null, the latter result will overwrite the previous result.

Of course, when processing to ApplicationContextAwareProcessor, the following Aware dependencies will be set.

  • EnvironmentAware
  • EmbeddedValueResolverAware
  • ResourceLoaderAware
  • ApplicationEventPublisherAware
  • MessageSourceAware
  • ApplicationContextAware

When processing to InitDestroyAnnotationBeanPostProcessor, the method modified by @ PostConstruct annotation will be found and executed.

3,invokeInitMethods

	protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {
        //If the InitializingBean interface is implemented, the afterpropertieset method is called
		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
			if (logger.isTraceEnabled()) {
				logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
			}
			if (System.getSecurityManager() != null) {
				try {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						((InitializingBean) bean).afterPropertiesSet();
						return null;
					}, getAccessControlContext());
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
				((InitializingBean) bean).afterPropertiesSet();
			}
		}

        //Call the method specified by init method declared in xml
		if (mbd != null && bean.getClass() != NullBean.class) {
			String initMethodName = mbd.getInitMethodName();
			if (StringUtils.hasLength(initMethodName) &&
					!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
					!mbd.isExternallyManagedInitMethod(initMethodName)) {
				invokeCustomInitMethod(beanName, bean, mbd);
			}
		}
	}

4. Post processing of calling BeanPostProcessor

Similar to preprocessing, it only calls the postProcessAfterInitialization method, which will not be repeated.

Therefore, the initialization process can be subdivided into

4, Destruction phase

When the container is closed, it will enter the Bean destruction phase, and the code starts from the close() method of AbstractApplicationContext

Don't hurry to enter the close() method. First look at the last method in the first section of the code:

	protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
		AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
        //If the Bean is not multiple instances and needs to be destroyed when the container is closed
		if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
			if (mbd.isSingleton()) {
                //Bind a DisposableBeanAdapter to the current Bean
				registerDisposableBean(beanName,
						new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
			}
			else {
				// A bean with a custom scope...
				Scope scope = this.scopes.get(mbd.getScope());
				if (scope == null) {
					throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
				}
				scope.registerDestructionCallback(beanName,
						new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
			}
		}
	}

	public void registerDisposableBean(String beanName, DisposableBean bean) {
		synchronized (this.disposableBeans) {
			this.disposableBeans.put(beanName, bean);
		}
	}

In registerdisposablebeaninifnecessary, bind the corresponding DisposableBeanAdapter object for each singleton Bean that needs to be destroyed when the container is closed. Finally, put these beans and their disposablebeanadapters into the map named disposableBeans for subsequent use.

Now let's enter the close() method of AbstractApplicationContext

You'll enter the destrysinglets method of DefaultSingletonBeanRegistry

	public void destroySingletons() {
		String[] disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
		
		for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
			destroySingleton(disposableBeanNames[i]);
		}
        //Omit some codes
	}

First get the names of all the beans to be destroyed, call the destroySingleton method one by one, go all the way down, and finally enter the destroyBean of DefaultSingletonBeanRegistry

The core sentence

		// Actually destroy the bean now...
		bean.destroy();
		

Obtain the DisposableBeanAdapter corresponding to the bean from disposableBeans according to beanName, and call its destroy method

    public void destroy() {
        //Call the method decorated with the @ PreDestroy annotation
        //Specifically, you can follow up the InitDestroyAnnotationBeanPostProcessor class
        if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
            for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
                processor.postProcessBeforeDestruction(this.bean, this.beanName);
            }
        }

        //If the DisposableBean interface is implemented, the destroy method is called
        if (this.invokeDisposableBean) {
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                    ((DisposableBean) this.bean).destroy();
                    return null;
                }, this.acc);
            } else {
                ((DisposableBean) this.bean).destroy();
            }
        }
        
        //Call the custom destroy method method in xml
        if (this.destroyMethod != null) {
            invokeCustomDestroyMethod(this.destroyMethod);
        } else if (this.destroyMethodName != null) {
            Method methodToCall = determineDestroyMethod(this.destroyMethodName);
            if (methodToCall != null) {
                invokeCustomDestroyMethod(methodToCall);
            }
        }
    }

Here, the Bean destruction process is basically over. Let's use a figure to summarize:

 

  5, Whole process of life cycle

We connect the whole stage

6, Code verification

Bean to be observed

public class A implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {
    private int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
        System.out.println("Attribute assignment");
    }

    private A() {
        System.out.println("instantiation ");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("@PostConstruct Specified method");
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("BeanClassLoaderAware.setBeanClassLoader");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("BeanFactoryAware.setBeanFactory");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("BeanNameAware.setBeanName");
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("InitializingBean.afterPropertiesSet");
    }

    public void initMethod() {
        System.out.println("xml in init-method Specified method");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("@PreDestroy Specified method");
    }

    @Override
    public void destroy() {
        System.out.println("DisposableBean.destroy");
    }

    public void destroyMethod() {
        System.out.println("xml in destroy-method Specified method");
    }

}

Custom BeanPostProcessor

public class BeanPostProcessorImpl implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("a")) {
            System.out.println("BeanPostProcessor.postProcessBeforeInitialization");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("a")) {
            System.out.println("BeanPostProcessor.postProcessAfterInitialization");
        }
        return bean;
    }
}

Custom InstantiationAwareBeanPostProcessorImpl

public class InstantiationAwareBeanPostProcessorImpl implements InstantiationAwareBeanPostProcessor {
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanName.equals("a")) {
            System.out.println("InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation");
        }
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (beanName.equals("a")) {
            System.out.println("InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation");
        }
        //If false is returned here, all beans will not be assigned properties
        return true;
    }
}

spring.xml configuration

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.yang.ym"/>

    <bean id="a" class="com.yang.ym.testBean.A" init-method="initMethod" destroy-method="destroyMethod">
        <property name="id" value="1"/>
    </bean>

    <bean class="com.yang.ym.testBean.BeanPostProcessorImpl"/>
    <bean class="com.yang.ym.testBean.InstantiationAwareBeanPostProcessorImpl"/>
</beans>

Test class

    @Test
    public void get() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        A a = (A) context.getBean("a");
        System.out.println("Close the container");
        context.close();
    }

Output results:

InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
 instantiation 
InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation

Attribute assignment

BeanNameAware.setBeanName
BeanClassLoaderAware.setBeanClassLoader
BeanFactoryAware.setBeanFactory

BeanPostProcessor.postProcessBeforeInitialization
@PostConstruct Specified method
InitializingBean.afterPropertiesSet
xml in init-method Specified method
BeanPostProcessor.postProcessAfterInitialization

Close the container
@PreDestroy Specified method
DisposableBean.destroy
xml in destroy-method Specified method

7, Summary

First remember the four major stages: instantiation, attribute assignment, initialization and destruction.

Within the life cycle, you can customize the Bean post processor and implement the interface for extension

  • Container level extension points, instantiaawarebeanpostprocessor and BeanPostProcessor.
  • Bean level extension points, Aware related interfaces, InitializingBean and DisposableBean.

In addition, you can specify extension points in the form of annotations or xml configuration

  • @PostConstruct annotation
  • @PreDestroy annotation
  • Init method in xml
  • Destroy method in xml

Then it will be much easier to remember the sequence of the life cycle:

getBean

  1. InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
  2. instantiation
  3. InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
  4. Attribute assignment
  5. Aware interface callback
  6. BeanPostProcessor.postProcessBeforeInitialization
  7. @Method specified by PostConstruct
  8. InitializingBean.afterPropertiesSet
  9. Method specified by init method in xml
  10. BeanPostProcessor.postProcessAfterInitialization

After closing the container

  1. @PreDestroy specified method
  2. DisposableBean.destroy
  3. Method specified by destroy method in xml

Topics: Java Spring