The most detailed explanation of Spring Bean's life cycle

Posted by gerkintrigg on Thu, 09 Dec 2021 04:05:32 +0100

Spring is the most popular and powerful lightweight framework in Java. The life cycle of Spring Bean is also a high-frequency interview question. Understanding the Spring Bean cycle can also better help us solve problems in daily development. Programmers should know that the basic container of spring is ApplicationContext. At the strong suggestion of many fans, I will analyze the life cycle of beans in ApplicationContext in this article. ApplicationContext is the implementation class of the top-level container interface BeanFactory. Therefore, we understand the life cycle logic of ApplicationContext and basically understand the life cycle logic of other types of containers.

1 Spring life cycle flowchart

Let's take a look at a complete life cycle flow chart of Spring beans. The following figure describes the key nodes from the initialization of beans by the Spring container to the destruction of beans by the Spring container.

As can be seen from the above figure, the basic idea of Spring Bean life cycle management is: before the Bean appears, prepare to operate the Bean's BeanFactory, and then operate the Bean. All beans will also be handed over to BeanFactory for management. Prepare the BeanPostProcessor as a callback for all Bean operations. In the process of Bean's complete life cycle management, the following main steps have been taken:

1.1 preparation phase before bean creation

Step 1: the Bean container finds the definition of Spring Bean and related configurations in the configuration file, such as the methods specified in init method and destroy method.
Step 2: instantiate the post processor related to the callback, such as beanfactoryprocessor, BeanPostProcessor, InstantiationAwareBeanPostProcessor, etc

1.2 create an instance of a Bean

Step 3: the Srping container uses the Java reflection API to create an instance of the Bean.
Step 4: scan the attributes declared by the Bean and resolve them.

1.3 start dependency injection

Step 5: start dependency injection, resolve all attributes to be assigned and assign values.
Step 6: if the Bean class implements the BeanNameAware interface, the setBeanName() method will be called by passing the name of the Bean.
Step 7: if the Bean class implements the BeanFactoryAware interface, the setBeanFactory() method will be called by passing an instance of the BeanFactory object.
Step 8: if any BeanPostProcessors object associated with BeanFactory has loaded Bean, the postProcessBeforeInitialization() method will be called before setting the Bean property.
Step 9: if the Bean class implements the InitializingBean interface, after setting all the Bean properties defined in the configuration file, the afterpropertieset() method will be called.

1.4 caching to Spring container

Step 10: if the Bean definition in the configuration file contains the init method attribute, the value of the attribute will be resolved to the method name in the Bean class and the method will be called.
Step 11: if any Bean post processor is attached to the Bean Factory object, the postProcessAfterInitialization() method is called.

1.5 destroy Bean instances

Step 12: if the Bean class implements the DisposableBean interface, the destroy() method will be called when the Bean reference is no longer needed by the Application.
Step 13: if the Bean definition in the configuration file contains the destroy method attribute, the corresponding method definition in the Bean class will be called.

2 code demonstration

Let's use a simple Bean to demonstrate and observe the complete life cycle of Spring Bean.

2.1 preparing the Author class

1. The first is a simple Bean, which calls its own methods and Bean level life cycle interface methods. To facilitate demonstration, it implements four interfaces: BeanNameAware, BeanFactoryAware, InitializingBean and DiposableBean, and adds two init method and destroy method methods, corresponding to the init method and destroy method of < Bean > in the configuration file. The specific codes are as follows:

package com.tom.lifecycle;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;

@Slf4j
@Data
public class Author implements BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean {
    private String name;
    private String address;
    private int age;

    private BeanFactory beanFactory;

    private String beanName;

    public Author() {
        log.info("[Constructor call Tom Constructor instantiation of class");
    }

    public void setName(String name) {
        log.info("[[injection attribute] name");
        this.name = name;
    }

    public void setAddress(String address) {
        log.info("[[injection attribute] address");
        this.address = address;
    }

    public void setAge(int age) {
        log.info("[[injection attribute] age");
        this.age = age;
    }

    // Method of implementing beanfactory aware interface
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("[BeanFactoryAware [interface] Call setBeanFactory method");
        this.beanFactory = beanFactory;
    }

    // Method of implementing BeanNameAware interface
    public void setBeanName(String beanName) {
        log.info("[BeanNameAware [interface] Call setBeanName method");
        this.beanName = beanName;
    }

    // Method for implementing the DiposibleBean interface
    public void destroy() throws Exception {
        log.info("[DiposibleBean [interface] Call destroy method");
    }

    // Method for implementing InitializingBean interface
    public void afterPropertiesSet() throws Exception {
        log.info("[InitializingBean [interface] Call afterPropertiesSet method");
    }

    // The initialization method specified by the init method attribute of < bean >
    public void beanInit() {
        log.info("[init-method]call<bean>of init-method Property");
    }

    // The initialization method specified by the destroy method attribute of < bean >
    public void beanDestory() {
        log.info("[destroy-method]call<bean>of destroy-method Property");
    }
}

Add the following content to the Spring configuration file:

    <bean id="author" class="com.tom.lifecycle.Author"
          init-method="beanInit"
          destroy-method="beanDestory"
          scope="singleton"
          p:name="Tom" p:address="Changsha, Hunan" p:age="18"/>
            

2.2 demonstrate the execution of beanfactoryprocessor

1. Create gpbeanfactoryprocessor class and implement beanfactoryprocessor interface. The specific code is as follows:

package com.tom.lifecycle;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

@Slf4j
public class GPBeanFactoryPostProcessor implements BeanFactoryPostProcessor{

    public GPBeanFactoryPostProcessor() {
        super();
        log.info("call BeanFactoryPostProcessor Implement class constructor!!");
    }

    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        log.info("BeanFactoryPostProcessor call postProcessBeanFactory method");
        BeanDefinition bd = configurableListableBeanFactory.getBeanDefinition("author");
        bd.getPropertyValues().addPropertyValue("age", "16");
    }
}

2. Add the following contents to the Spring configuration file:

    <bean id="beanFactoryPostProcessor" class="com.tom.lifecycle.GPBeanFactoryPostProcessor" />

3. Write the test class beanlife cycletest. The specific code is as follows:

package com.tom.lifecycle;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@Slf4j
public class BeanLifeCycleTest {

    public static void main(String[] args) {

        log.info("====== Start initialization Spring container ========");

        ApplicationContext factory = new ClassPathXmlApplicationContext("application-beans.xml");

        log.info("====== initialization Spring Container success ========");

        //Get Author instance
        Author author = factory.getBean("author", Author.class);

        log.info(author.toString());

        log.info("====== Start destruction Spring container ========");

        ((ClassPathXmlApplicationContext) factory).registerShutdownHook();
    }

}

4. Operation results

The operation results are as follows:

15:49:12.477 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - call BeanPostProcessor Implement class constructor!!
15:49:12.494 [main] INFO com.tom.lifecycle.Author - [Constructor call Tom Constructor instantiation of class
15:49:12.527 [main] INFO com.tom.lifecycle.Author - [[injection attribute] address
15:49:12.528 [main] INFO com.tom.lifecycle.Author - [[injection attribute] age
15:49:12.528 [main] INFO com.tom.lifecycle.Author - [[injection attribute] name
15:49:12.528 [main] INFO com.tom.lifecycle.Author - [BeanNameAware [interface] Call setBeanName method
15:49:12.528 [main] INFO com.tom.lifecycle.Author - [BeanFactoryAware [interface] Call setBeanFactory method
15:49:12.528 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - BeanPostProcessor Interface method postProcessBeforeInitialization Make changes to properties
15:49:12.528 [main] INFO com.tom.lifecycle.Author - [InitializingBean [interface] Call afterPropertiesSet method
15:49:12.528 [main] INFO com.tom.lifecycle.Author - [init-method]call<bean>of init-method Property
15:49:12.528 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - BeanPostProcessor Interface method postProcessAfterInitialization Make changes to properties
15:49:12.531 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - ====== initialization Spring Container success ========
15:49:12.531 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - Author(name=Tom, address=Changsha, Hunan, age=18, beanFactory=org.springframework.beans.factory.support.DefaultListableBeanFactory@26653222: defining beans [beanPostProcessor,author]; root of factory hierarchy, beanName=author)
15:49:12.531 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - ====== Start destruction Spring container ========
15:49:12.532 [Thread-0] INFO com.tom.lifecycle.Author - [DiposibleBean [interface] Call destroy method
15:49:12.533 [Thread-0] INFO com.tom.lifecycle.Author - [destroy-method]call<bean>of destroy-method Property

We see that the whole execution is consistent with the flowchart we drew at the beginning. But why should we implement the beanfactoryprocessor interface? The source code of beanfactoryprocessor is as follows:

package org.springframework.beans.factory.config;

import org.springframework.beans.BeansException;

public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}

The BeanFactory postprocessor interface has only one postprocessbeanfactory () method. BeanFactory postprocessor: it can be modified after the BeanFactory standard is initialized. All bean definitions will be loaded, but the bean has not been instantiated. This method allows you to overwrite or add properties or even quickly initialize the bean. You may not know what postprocessbeanfactory () is for the first time. To fully understand the function of this method, let's go to the source code of BeanFactory postprocessor and understand the parameters of postProcessBeanFactory(). We can use these parameters to do some operations.

From the parameters, there is only one ConfigurableListableBeanFactory class, which can provide the functions of analyzing, modifying Bean definitions and pre instantiating singletons. Let's go to the source code of ConfigurableListableBeanFactory:

public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {

        //Ignore the given injection dependency type, such as String
    void ignoreDependencyType(Class<?> var1);

        //Slightly given the injection dependent interface. This is usually used by ApplicationContext to register dependencies, which can be implemented in a variety of ways. For example, BeanFactory passes BeanFactory aware, and ApplicationContext passes ApplicationContextAware. By default, only BeanFactoryAware interface is ignored, and other interfaces need to be ignored. Call this method
    void ignoreDependencyInterface(Class<?> var1);

        //Registering a specific type dependency is accompanied by the corresponding Autowired value. This is a factory / context reference prepared to be used for beans that should be defined in Autowire rather than in this factory. For example, resolve the dependency of ApplicationContext type to the ApplicationContext instance where the Bean is located. Note ~ there is no such default type registered in the ordinary BeanFactory, not even the BeanFactory interface itself
    void registerResolvableDependency(Class<?> var1, Object var2);

        //Confirm whether the specified Bean is an Autowire candidate and will be injected into other beans that declare dependencies of matching types
    boolean isAutowireCandidate(String var1, DependencyDescriptor var2) throws NoSuchBeanDefinitionException;

        //Returns the registered Bean definition according to the specified beanName, allowing access to its property value and constructor parameter value (which can be modified during BeanFactory post-processing). The returned BeanDefinition object should not be a copy, but registered in the original factory. This means that it can be converted to a more specific implementation type if needed. Note that this method can only obtain the local factory BeanDefinition
    BeanDefinition getBeanDefinition(String var1) throws NoSuchBeanDefinitionException;

        //Freeze all Bean definitions and send a signal to the registered Bean definitions to tell them that they will not be modified and processed further in the future. It allows Factory to actively cache Bean definition metadata
    void freezeConfiguration();

        //Returns whether the beandefinition of the factory is frozen
    boolean isConfigurationFrozen();

        //Ensure that all non lazy loaded singleton beans are instantiated, including factorybeans
    void preInstantiateSingletons() throws BeansException;
}

Through the above demonstration and analysis, we should be able to understand the role of ConfigurableListableBeanFactory, which is basically the operations defined for beans. So far, we haven't seen the calls of BeanPostProcessor and instantiaawarebeanpostprocessor. Let's supplement the implementation of BeanPostProcessor and instantiaawarebeanpostprocessor, and then look at the complete execution process

2.3 implementation of BeanPostProcessor

Create GPBeanPostProcessor class and implement BeanPostProcessor interface. The specific code is as follows:

package com.tom.lifecycle;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

@Slf4j
public class GPBeanPostProcessor implements BeanPostProcessor {

    public GPBeanPostProcessor(){
        log.info("call BeanPostProcessor Implement class constructor!!");
    }
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
        log.info("BeanPostProcessor Interface method postProcessBeforeInitialization Make changes to properties");
        return o;
    }

    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
        log.info("BeanPostProcessor Interface method postProcessAfterInitialization Make changes to properties");
        return o;
    }
}

ApplicationContext can automatically detect beans that implement BeanPostProcessor in BeanDefinition, and apply these beans to subsequent Bean creation. Ordinary BeanFactory allows the post processor to be programmatically registered and applied to all Bean creation through the factory. There are two main methods in the BeanPostProcessor interface:

Method nameexplain
postProcessBeforeInitializationThis BeanPostProcessor is applied before the Bean instantiates a callback (such as the afterpropertieset of InitializingBean or a custom init method)
postProcessAfterInitializationThis BeanPostProcessor is applied after the bean instantiation callback (such as the afterpropertieset of InitializingBean or a custom init method)

2.4 implementation of instantiaawarebeanpostprocessor

Create the gpinstantiaawarebeanpostprocessor class and implement the instantiaawarebeanpostprocessoradapter interface. The specific code is as follows:

package com.tom.lifecycle;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;

import java.beans.PropertyDescriptor;

@Slf4j
public class GPInstantiationAwareBeanPostProcessor  extends InstantiationAwareBeanPostProcessorAdapter {

    public GPInstantiationAwareBeanPostProcessor() {
        super();
        log.info("call InstantiationAwareBeanPostProcessorAdapter Implement class constructor!!");
    }

    // The interface method and the instantiation of Bean are called before.
    @Override
    public Object postProcessBeforeInstantiation(Class beanClass,String beanName) throws BeansException {
        log.info("InstantiationAwareBeanPostProcessor call postProcessBeforeInstantiation method");
        return null;
    }

    // After the interface method is instantiated, Bean is called.
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        log.info("InstantiationAwareBeanPostProcessor call postProcessAfterInitialization method");
        return bean;
    }

    // Interface method, called when setting a property
    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        log.info("InstantiationAwareBeanPostProcessor call postProcessPropertyValues method");
        return pvs;
    }
}

After implementing the Bean of the instantiaawarebeanpostprocessoradapter, you can do some verification or supplement some content or inject the Bean wrapper agent after the instantiation is successful. After implementing the Bean of instantiaawarebeanpostprocessoradapter, it will not affect the container's normal processing of each instantiated Bean. Its subclasses only override the methods of the parent class as needed.

Note that this base class is recommended only when the instantiaawarebeanpostprocessor function is actually required. If all we need is a simple BeanPostProcessor function, we can directly implement a simpler interface.

The following describes in detail all methods in the instantiaawarebeanpostprocessoradapter interface:

Method nameexplain
postProcessBeforeInstantiationApply this BeanPostProcessor before instantiating the target Bean. The returned Bean may be a proxy instead of the target Bean, which effectively suppresses the default instantiation of the target Bean. If this method returns a non empty object, the Bean creation process will be short circuited. The only further processing applied is the BeanPostProcessor.postProcessAfterInitialization(java.lang.Object, java.lang.String) method (after changing the Bean's life cycle and instantiating, directly enter BeanPostProcessor.postProcessAfterInitialization). The callback comes from the configured BeanPostProcessors. This callback will only be applied to beandefinitions with Bean Class. In particular, it does not apply to beans that adopt "factory method". Post processors can implement the extended smartinstantiaawarebeanpostprocessor interface to predict the type of Bean objects they will return
postProcessPropertyValuesPost process the given property value before the factory applies the given property value to the given Bean. It allows you to check whether all dependencies have been met, for example, on the Setter method of the Bean property based on a @ Required. It also allows you to replace the property value to be applied. Usually, you can create a new MutablePropertyValues instance based on the original PropertyValues, add or delete it Delete specific values
postProcessAfterInitializationInitialize the callback in the Bean (such as the afterpropertieset of InitializingBean or the customized init method) After that, apply this BeanPostProcessor to a new Bean instance. The Bean has configured the property value, and the returned Bean instance may have been wrapped. < br / > if it is a FactoryBean, this callback will be called by the FactoryBean instance and other objects created by the FactoryBean. This post processor can check and decide whether to apply factoryb through the corresponding FactoryBean instance This callback will be called after a short circuit is triggered by the instantiaawarebeanpostprocessor

2.5 modifying configuration files

The complete configuration file is as follows:

<?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:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
            http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

        <bean id="beanPostProcessor" class="com.tom.lifecycle.GPBeanPostProcessor" />

        <bean id="beanFactoryPostProcessor" class="com.tom.lifecycle.GPBeanFactoryPostProcessor" />

        <bean id="instantiationAwareBeanPostProcessor" class="com.tom.lifecycle.GPInstantiationAwareBeanPostProcessor" />


        <bean id="author" class="com.tom.lifecycle.Author"
                init-method="beanInit"
                destroy-method="beanDestory"
                scope="singleton"
                p:name="Tom" p:address="Changsha, Hunan" p:age="18"/>

</beans>

2.6 operation results

Finally, we run the beanlife cycletest test class again and see the following results:

15:56:20.030 [main] INFO com.tom.lifecycle.GPBeanFactoryPostProcessor - call BeanFactoryPostProcessor Implement class constructor!!
15:56:20.045 [main] INFO com.tom.lifecycle.GPBeanFactoryPostProcessor - BeanFactoryPostProcessor call postProcessBeanFactory method
15:56:20.046 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - call BeanPostProcessor Implement class constructor!!
15:56:20.047 [main] INFO com.tom.lifecycle.GPInstantiationAwareBeanPostProcessor - call InstantiationAwareBeanPostProcessorAdapter Implement class constructor!!
15:56:20.051 [main] INFO com.tom.lifecycle.GPInstantiationAwareBeanPostProcessor - InstantiationAwareBeanPostProcessor call postProcessBeforeInstantiation method
15:56:20.052 [main] INFO com.tom.lifecycle.Author - [Constructor call Tom Constructor instantiation of class
15:56:20.069 [main] INFO com.tom.lifecycle.GPInstantiationAwareBeanPostProcessor - InstantiationAwareBeanPostProcessor call postProcessPropertyValues method
15:56:20.092 [main] INFO com.tom.lifecycle.Author - [[injection attribute] address
15:56:20.092 [main] INFO com.tom.lifecycle.Author - [[injection attribute] age
15:56:20.092 [main] INFO com.tom.lifecycle.Author - [[injection attribute] name
15:56:20.092 [main] INFO com.tom.lifecycle.Author - [BeanNameAware [interface] Call setBeanName method
15:56:20.092 [main] INFO com.tom.lifecycle.Author - [BeanFactoryAware [interface] Call setBeanFactory method
15:56:20.093 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - BeanPostProcessor Interface method postProcessBeforeInitialization Make changes to properties
15:56:20.093 [main] INFO com.tom.lifecycle.Author - [InitializingBean [interface] Call afterPropertiesSet method
15:56:20.093 [main] INFO com.tom.lifecycle.Author - [init-method]call<bean>of init-method Property
15:56:20.093 [main] INFO com.tom.lifecycle.GPBeanPostProcessor - BeanPostProcessor Interface method postProcessAfterInitialization Make changes to properties
15:56:20.093 [main] INFO com.tom.lifecycle.GPInstantiationAwareBeanPostProcessor - InstantiationAwareBeanPostProcessor call postProcessAfterInitialization method
15:56:20.097 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - ====== initialization Spring Container success ========
15:56:20.098 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - Author(name=Tom, address=Changsha, Hunan, age=16, beanFactory=org.springframework.beans.factory.support.DefaultListableBeanFactory@26653222: defining beans [beanPostProcessor,beanFactoryPostProcessor,instantiationAwareBeanPostProcessor,author]; root of factory hierarchy, beanName=author)
15:56:20.098 [main] INFO com.tom.lifecycle.BeanLifeCycleTest - ====== Start destruction Spring container ========
15:56:20.099 [Thread-0] INFO com.tom.lifecycle.Author - [DiposibleBean [interface] Call destroy method
15:56:20.100 [Thread-0] INFO com.tom.lifecycle.Author - [destroy-method]call<bean>of destroy-method Property

3. Running sequence diagram of spring bean life cycle

Finally, let's take a look at the complete execution sequence diagram:

Topics: Java Spring source code