An overview,

1. Bean life cycle

The Bean life cycle includes the process of Bean creation, survival and destruction. Creation is the process of instantiation, survival includes attribute assignment and initialization, so the Bean life cycle is: instantiation, attribute assignment, initialization and destruction, and this process is different from singleton and multi-instance:

[1] Singleton beans

  • Instantiation: Instantiates objects when the container is started
  • Live: Bean objects live as long as the container exists (coexists with the container)
  • Destroy: Destroy the container as soon as it is closed

[2] Multi-example beans

  • Instantiate: Instantiate an object when it is acquired
  • Live: Objects live as long as they are in use
  • Destruction: Collected by the Java garbage collector when an object has not been used for a long time and no other object references it

2. Deal with the life cycle

In our development process, we generally do some processing before and after initialization, processing in many ways, can use annotations, can also use the implementation class, summarized as the following four points:

[1] Specify the method

You can specify initialization and destruction methods:

  • Specified by the @bean annotationinitMethodanddestroyMethod

[2] Implement the interface

Implementation interface:

  • InitializingBean interface: Defines the initialization logic

  • DisposableBean interface: Define the destruction logic

[3] JSR 250 annotation specification

JSR stands for Java Specification Requests, and the JSR 250 Specification contains annotations for injecting resources into endpoint implementation classes and annotations for managing the application lifecycle.

Using JSR:

  • PostConstruct annotation: Execute the initialization method after the Bean has been created and assigned

  • PreDestroy Note: Remind us to do a cleanup before the container destroys the container

[4] Post-processor

BeanPostProcessor interface: The Bean’s post-processor that does some processing before and after the Bean is initialized

  • PostProcessBeforeInitialization: before initialization work

  • PostProcessAfterInitialization: after initialization work

Ii. Case analysis

[1] Specify the method

You can specify initialization and destruction methods through the @bean annotation: initMethod and destroyMethod, using the Dog object as an example:

/ / start the class
public class MainTest {
    @Test
    public void TestMain(a) {
        // Create an IOC container
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        // applicationContext.getBean("dog");applicationContext.close(); }}// The Bean to inject
public class Dog {
    public Dog(a){
        System.out.println("dog create");
    }
    public void init(a){
        System.out.println("dog init");
    }
    public void destroy(a){
        System.out.println("dog destroy"); }}/ / configuration class
@Configuration
public class AppConfig {
    // @Scope("prototype")
    // Specify the initialization and destruction methods via the @bean annotation
    @Bean(initMethod = "init",destroyMethod = "destroy")
    public Dog dog(a){
        return newDog(); }}Copy the code

This is the case in the single-instance scenario, where the object is instantiated when the container is started and destroyed when the container is closed. As you can see, @bean (initMethod = “init”,destroyMethod = “destroy”) can specify the initialization and destruction methods.

If you have multiple instances, leave @Scope(“prototype”) out of the configuration class. Multiple instances instantiate objects when they are fetched. So you need to start classes in the applicationContext) getBean (” dog “); Release, get the object, run the startup class, create the Bean and initialize the Bean, and destroy is collected by the Java garbage collector when the object has not been used for a long time and no other object references it.

[2] Implement the interface

Implement the Bean object InitializingBean, DisposableBean interface, override destroy and afterPropertiesSet methods to destroy and initialize the Bean, note the @Component annotation to inject the Bean into the container, And use the @ComponentScan annotation in the configuration class to scan, using the CAT object as an example:

/ / start the class
@Test
public void TestMain(a) {
    // Create an IOC container
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    applicationContext.close();
}

// The Bean to inject
@Component
public class Cat implements InitializingBean.DisposableBean {
    public Cat(a){
        System.out.println("cat create");
    }
    / / destruction of Bean
    public void destroy(a) throws Exception {
        System.out.println("cat destroy");
    }
    // Initialize the Bean
    public void afterPropertiesSet(a) throws Exception {
        System.out.println("cat afterPropertiesSet"); }}/ / configuration class
@ComponentScan("bean")
@Configuration
public class AppConfig {}Copy the code

Implement the InitializingBean, DisposableBean interface, override the destroy and afterPropertiesSet methods to destroy and initialize the bean.

[3] Use JSR250 annotation specification

Use the JSR250 annotation specification to create, assign, and destroy objects using the @postConstruct and @Predestroy annotations. For example, pig:

/ / start the class
@Test
public void TestMain(a) {
    // Create an IOC container
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    applicationContext.close();
}

// The Bean to inject
@Component
public class Pig {
    public Pig(a){
        System.out.println("pig create");
    }
    // @postconstruct: called after the object is created and assigned
    @PostConstruct
    public void init(a){
        System.out.println("pig postConstruct");
    }
    // @predestroy: before the container removes the object
    @PreDestroy
    public void destroy(a){
        System.out.println("pig destroy"); }}/ / configuration class
@ComponentScan("bean")
@Configuration
public class AppConfig {}Copy the code

The @postConstruct annotation is used to initialize the method and the @preDestroy annotation is used to initialize the method. Run the start class and you can see that initialization and destruction are performed:

[4] Post-processor

Post processor is do some work before and after the Bean is initialized, realize the BeanPostProcessor class, overriding methods, postProcessBeforeInitialization before initialization work, PostProcessAfterInitialization after initialization work, added in the pig object instance:

@Component      // Add the post-processor to the container
public class MyBeanPostProcessor implements BeanPostProcessor {
    / / postProcessBeforeInitialization: before initialization work
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization" + beanName + "= = =" + bean);
        return bean;
    }
    / / postProcessAfterInitialization: after initialization work
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization" + beanName + "= = =" + bean);
        returnbean; }}Copy the code

Execute the corresponding code before and after the pig object is initialized as follows:

Third, source tracking

BeanPostProcessor is a very important class in Spring source code. Let’s see how spring implements Bean initialization

[1] Refresh the container

The IOC container constructor calls the refresh method to refresh the container

public AnnotationConfigApplicationContext(String... basePackages) {
    this(a);this.scan(basePackages);
    // Refresh the container
    this.refresh();
}
Copy the code

[2] Get the instance

Call the refresh method of finishBeanFactoryInitialization initialize all singleton

public void refresh(a) throws BeansException, IllegalStateException {
    synchronized(this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
        this.prepareRefresh();
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
        this.prepareBeanFactory(beanFactory);

        try {
            this.postProcessBeanFactory(beanFactory);
            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            this.invokeBeanFactoryPostProcessors(beanFactory);
            this.registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();
            this.initMessageSource();
            this.initApplicationEventMulticaster();
            this.onRefresh();
            this.registerListeners();
            // Initialize all singletons
            this.finishBeanFactoryInitialization(beanFactory);
            this.finishRefresh();
        } catch (BeansException var10) {
            if (this.logger.isWarnEnabled()) {
                this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
            }

            this.destroyBeans();
            this.cancelRefresh(var10);
            throw var10;
        } finally {
            this.resetCommonCaches(); contextRefresh.end(); }}}Copy the code
  1. topreInstantiateSingletonsIn a class calledgetBeanmethods
  2. calldoGetBean
  3. callgetSingletonGet a single instance
  4. If not, callcreateBeanCreate an instance
this.getBean(beanName);
Copy the code
public Object getBean(String name) throws BeansException {
    return this.doGetBean(name, (Class)null, (Object[])null.false);
}
Copy the code
if (mbd.isSingleton()) {
    // Get a single instance
    sharedInstance = this.getSingleton(beanName, () -> {
        try {
            // If not, call createBean to create the instance
            return this.createBean(beanName, mbd, args);
        } catch (BeansException var5) {
            this.destroySingleton(beanName);
            throwvar5; }}); bean =this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
Copy the code
// Create an instance
beanInstance = this.doCreateBean(beanName, mbdToUse, args);
Copy the code

[3] Create an instance

So how do we do that? Let’s go ahead and look at the source code, and trace back to the initializeBean method in the doCreateBean class, and there’s a populateBean method before that, right

  • PopulateBean: Assigns a value to the Bean property
  • InitializeBean: A call equivalent to a post-processor
try {
    // Assign a value to the Bean property
    this.populateBean(beanName, mbd, instanceWrapper);
    // equivalent to a post-processor call
    exposedObject = this.initializeBean(beanName, exposedObject, mbd);
} catch (Throwable var18) {
    if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
        throw (BeanCreationException)var18;
    }

    throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
}
Copy the code

Call initializeBean method of invokeInitMethods method performs initialization method, in the realization of the above mentioned interface, using annotations, custom initialization procedures are carried out in this method, look at this method before and after:

  • applyBeanPostProcessorsBeforeInitialization
  • applyBeanPostProcessorsAfterInitialization
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    if(System.getSecurityManager() ! =null) {
        AccessController.doPrivileged(() -> {
            this.invokeAwareMethods(beanName, bean);
            return null;
        }, this.getAccessControlContext());
    } else {
        this.invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null| |! mbd.isSynthetic()) { wrappedBean =this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
    }

    try {
        // Execute the initialization method
        this.invokeInitMethods(beanName, wrappedBean, mbd);
    } catch (Throwable var6) {
        throw newBeanCreationException(mbd ! =null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
    }

    if (mbd == null| |! mbd.isSynthetic()) { wrappedBean =this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}
Copy the code

Click on these two methods to view the source code:

applyBeanPostProcessorsBeforeInitialization

  1. I’m going to go through all the BeanPostProcessors
  2. Perform BeforeInitialization one by one
  3. Null is returned directly, will not execute BeanPostProcessors. PostProcessBeforeInitialization
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
    Object result = existingBean;

    Object current;
    / / get all BeanPostProcessors traverse, each performing BeforeInitialization, null is returned directly, do not perform the back of the BeanPostProcessors. PostProcessBeforeInitialization
    for(Iterator var4 = this.getBeanPostProcessors().iterator(); var4.hasNext(); result = current) {
        BeanPostProcessor processor = (BeanPostProcessor)var4.next();
        current = processor.postProcessBeforeInitialization(result, beanName);
        if (current == null) {
            returnresult; }}return result;
}
Copy the code

applyBeanPostProcessorsAfterInitialization

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
    Object result = existingBean;

    Object current;
    for(Iterator var4 = this.getBeanPostProcessors().iterator(); var4.hasNext(); result = current) {
        BeanPostProcessor processor = (BeanPostProcessor)var4.next();
        current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            returnresult; }}return result;
}
Copy the code

Four,

BeanPostProcessors principle:

  1. throughgetBeanFor instance
  2. If no, passcreateBeanCreate an instance
  3. indoCreateBeanClass throughpopulateBeanAssign a value to an instance attribute
  4. callinitializeBeanIn the methodinvokeInitMethodsMethod performs the initialization method
  5. Executed before and after instance initializationapplyBeanPostProcessorsBeforeInitialization,applyBeanPostProcessorsAfterInitialization, which is what the post-processor does before and after the instance is initialized