[JAVA] [Spring] Bean life cycle-from generation to destruction-

Overview

This article summarizes the bean life cycle. It's a rather internal story. The following keywords are assumed to be known.

By the way, I took the Spring Professional v5.0 Exam sponsored by pivotal the other day, so this is a summary of what I learned at that time.

Bean life cycle

The life cycle of a bean is roughly divided into three.

  1. Initialization phase
  2. Usage phase
  3. End phase

Initialization phase

Mainly in this phase

--Read / rewrite bean definition --Bean generation & DI --Initialization process

to hold. This phase is the most complex. The general flow is like this

bean-lifecycle.001.jpeg

Let's take a closer look.

Read bean definition

First, the information required to generate the bean is read.

--Java Config with @Configuration --Classes with @Component, @Controller, @ RestController, @Service, @Repository --XML file with Bean definition

Based on the information read here, something like a Bean definition information list is created. (The entity is BeanDefinition ) Map of the object?) Bean implementation class, scope, dependent beans, fields, etc. are written in the bean definition information list.

Class Name Scope Depends on Property ...
com.example.hoge.AImpl a Singleton b url=${url} ...
com.example.fuga.BImpl b Prototype c age=10 ...
com.example.piyo.CImpl c Singleton ...

Rewriting bean definition

Here, the bean definition information is rewritten. It is an image that the Bean definition information list created in the previous step is modified. For example, the process of embedding a property value in a placeholder declared with @Value is performed here.

AImpl.java


@Component("a")
public class AImpl implements A {

    @Value("${url}")
    private String url;
}

application.properties


url=http://www.sample.com

Actually, BeanDefinition is modified based on the property value injected by @Value. BeanFactoryPostProcessor realizes rewriting of bean definition information. factory / config / BeanFactoryPostProcessor.java). Processing is performed by the class that implements the BeanFactoryPostProcessor interface.

public interface BeanFactoryPostProcessor {
   void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
}

PropertySourcesPlaceholderConfigurer that implements BeanFactoryPostProcessor for the process of embedding property value with @Value /springframework/context/support/PropertySourcesPlaceholderConfigurer.java) Class is responsible. (Refer to the link for detailed processing) By the way, when using @Value, it is necessary to define a bean that returns PropertySourcesPlaceholderConfigurer. (It seems that it is not necessary to explicitly define it if it is Spring Boot and Spring 4.3 or later) This bean must be defined in a static method because Spring executes it before the bean is created.

@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
    return new PropertySourcesPlaceholderConfigurer();
}

At this point, the generation of Bean definition information is complete. Actually, the bean has not been generated yet. Beans are generated from the next phase.

Bean generation & DI

At this point, a bean instance is finally created and DI (Dependency Injection) is performed. DI is performed in the following order.

  1. Constructor injection
  2. Field injection
  3. Setter injection

Initialization process after bean generation

Initialization processing is performed after bean generation. As a feature of the processing that can be performed here, the initialization processing can be performed using the generated Bean. For example, the processing of the method with @PostConstruct is performed at this stage. In the method with @PostConstruct, you can write the initialization process using the injected field.

@Component
public class HogeServiceImpl implements HogeService {

    private final Fuga fuga;

    @Autowired
    public HogeServiceImpl(Fuga fuga) {
        this.fuga = fuga;
    }

    //Called after DI is over
    //Return value must be void, no argument
    @PostConstruct
    public void populateCache() {
        ...
    }
}

Pre-processing and post-processing can be inserted before and after the initialization processing of this step. This process is BeanPostProcessor The class that implements the interface does it.

public interface BeanPostProcessor {
    //Preprocessing
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    //Post-processing
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

Usage phase

This is the phase in which the bean is actually used.

ApplicationContext context = SpringApplication.run(AppConfig.class);
HogeService service = context.getBean("hogeService", HogeService.class); //Get Bean
service.do(); //Use of beans

End phase

This is the phase in which the DI container is destroyed.

Pre-discard processing

Processing is performed before destroying the DI container. Methods with @PreDestroy added are processed at this stage.

@Component
public class HogeServiceImpl implements HogeService {

    //Called before the DI container is destroyed
    //Return value must be void, no argument
    @PreDestroy
    public void clearCache() {
        ...
    }
}

Discard DI container

When the close method of ConfigurableApplicationContext is called The DI container is destroyed.

context.close();

If generated by SpringApplication.run (), it hooks to the JVM shutdown and destroys the DI container.

ConfigurableApplicationContext context = SpringApplication.run(AppConfig.class);
//ShutdownHook is registered in the JVM

Reference material

Recommended Posts

[Spring] Bean life cycle-from generation to destruction-
Spring bean life cycle