Analysis of Bean life cycle source code in Spring

Posted by AbsolutelyFreeW on Sat, 20 Nov 2021 03:28:35 +0100

Bean life cycle

Compared with the life cycle of beans created by ourselves, they are instantiated through the new keyword and recycled when they are no longer used. The life cycle of beans in the Spring container is more complex. Let's take a look at the following bean life cycle diagram.

As a supplement to the above figure, there should be another step between BeaFactoryAware's setBeanFactory() and pre initialization beanpostprocessor: call the setApplicationContext() method of ApplicationContextAware.

As can be seen from the above figure, the Bean life cycle goes through many stages, but most of these stages are optional. For example, if a Bean implements the setBeanFactory method of the BeanFactoryAware interface, the life of the Bean will go through this stage. If it is not implemented, it will not.

Let's take a look at how to implement a bean that has gone through all the above life cycle stages

First define the Bean object and implement the following interfaces

  • BeanNameAware
  • BeanFactoryAware
  • ApplicationContextAware
  • InitializingBean
  • DisposableBean
@Component
public class Car implements BeanNameAware,BeanFactoryAware,
							ApplicationContextAware,InitializingBean,DisposableBean {
    
    //Seat is also a simple bean object
    private Seat seat;

    public Car(){
        System.out.println("car instance...");
    }

    public Seat getSeat() {
        return seat;
    }

    @Autowired
    public void setSeat(Seat seat) {
        System.out.println("Fill properties");
        this.seat = seat;
    }

    // Custom initialization method
    public void init(){
        System.out.println("car ... init...");
    }

    // Custom destruction method
    public void detory(){
        System.out.println("car ... detory...");
    }

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

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

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("ApplicationContextAware...setApplicationContext()");
    }

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

    @Override
    public void destroy() throws Exception {
        System.out.println("InitializingBean...destroy()");
    }
}

After you have customized initialization methods and destruction methods, you need to configure them. You can specify them in the configurator class through @ bean (initmethod = "init", destroymethod = "destroy"), or directly add @ PostConstruct or @ PreDestroy annotations to the methods

//After the object is created and assigned, it is called.
@PostConstruct
public void init(){
    System.out.println("car....@PostConstruct...");
}

//Before removing objects from the container
@PreDestroy
public void detory(){
    System.out.println("car....@PreDestroy...");
}

Then define a post processor of the Bean: BeanPostProcessor

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization..."+beanName+"..."+bean);
        return bean;
    }

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

Finally, write a test class and observe the running results

@Test
public void test(){
    //1. Create ioc container
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
    System.out.println("Container creation complete...");
    applicationContext.getBean("car");
    //Close the container
    applicationContext.close();
}

The output result is:

car instance...
Fill properties
BeanNameAware...setBeanName()
DisposableBean...setBeanFactory()
ApplicationContextAware...setApplicationContext()
postProcessBeforeInitialization...car...cn.zgc.spring.annotation.beans.Car@22b49166
InitializingBean...afterPropertiesSet()
car ... init...
postProcessAfterInitialization...car...cn.zgc.spring.annotation.beans.Car@22b49166
 Container creation complete...
InitializingBean...destroy()
car ... detory...

Viewing the output results, you can see that the life cycle of Car is consistent with the figure above

##Life cycle construction principle
Let's take a look at how it is implemented in the Spring source code. Let's make a breakpoint in the custom Bean post processor MyBeanPostProcessor to see the method call stack

Here's a reminder. It's cumbersome to look at the source code of Spring. It's easy to get caught up in the details. Therefore, we should grasp the key code and sort out the overall context, and we shouldn't and don't miss the details too much

Here, we mainly take a look at the doCreateBean method of AbstractAutowireCapableBeanFactory, in which there is such a piece of code

//Instantiate bean s
if (mbd.isSingleton()) {
    instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
    instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
......
try {
    // Populate the properties of the bean
    this.populateBean(beanName, mbd, instanceWrapper);
    if (exposedObject != null) {
        exposedObject = this.initializeBean(beanName, exposedObject, mbd);
    }
}

Continue to see what is done in the initializeBean method

// The callback implements the methods in the xxxAware interface
this.invokeAwareMethods(beanName, bean);
......    
// Call the postProcessorsBeforeInitialization method of BeanPostProcessor
wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
......
try {
    //Call the initialization method (custom initialization method or implement the initializingbean interface)
    invokeInitMethods(beanName, wrappedBean, mbd);
}catch (Throwable ex) {
    ....
}

if (mbd == null || !mbd.isSynthetic()) {
    // Call the postProcessorsAfterInitialization method of BeanPostProcessor
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

Next, combined with the source code, the life cycle process of Bean is given in the form of pseudo code, but it does not include two destruction stages

 // Create Bean 
 new Bean();
 // Assign properties to bean s
 populateBean(beanName, mbd, instanceWrapper);
 // Here is a series of operations on bean s
 initializeBean() {
    applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    invokeInitMethods(beanName, wrappedBean, mbd);Perform custom initialization
     {
        if(bean instanceof InitializingBean){
            ((InitializingBean) bean).afterPropertiesSet();
        }
        if(mbd.getInitMethodName()!=null){ //Custom initialization method execution
            invokeCustomInitMethod(beanName, bean, mbd);
        }
     }
    applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
 }

Summary

In Spring, the life cycle of a bean is a long process, with many steps, and each step can control the bean. Understanding this process is very helpful for us to read the Spring source code and expand Spring.

Topics: Spring source code analysis