banner
biuaxia

biuaxia

"万物皆有裂痕,那是光进来的地方。"
github
bilibili
tg_channel

[Reprint] Six ways to get things done during SpringBoot initialization!

title: 【Reprint】Six Ways to Do Something During SpringBoot Initialization!
date: 2021-08-24 15:19:00
comment: false
toc: true
category:

  • Java
    tags:
  • Reprint
  • Java
  • SpringBoot
  • Initialization
  • Ways
  • Methods
  • Container
  • Refresh
  • Listener
  • Startup
  • Project
  • Event

This article is reprinted from: Six Ways to Do Something During SpringBoot Initialization!


Introduction#

In actual work, it is often necessary to perform some initialization operations when the project starts, such as initializing thread pools, preloading encryption certificates, etc.

So the classic question arises, which is also a common question asked by interviewers: What methods are there to do something when a Spring Boot project starts?

There are many methods, and below are a few common ones.

image

1. Listening to Container Refresh Completion Extension Point ApplicationListener<ContextRefreshedEvent>#

The ApplicationContext event mechanism is implemented through the observer design pattern, using the ApplicationEvent and ApplicationListener interfaces to realize the event mechanism of ApplicationContext.

Some built-in events in Spring are as follows:

  1. ContextRefreshedEvent: This event is published when the ApplicationContext is initialized or refreshed. It can also occur using the refresh() method in the ConfigurableApplicationContext interface. Here, initialization means: all Beans are successfully loaded, post-processing Beans are detected and activated, all Singleton Beans are pre-instantiated, and the ApplicationContext container is ready for use.
  2. ContextStartedEvent: This event is published when the ApplicationContext is started using the start() method in the ConfigurableApplicationContext (a sub-interface of ApplicationContext). You can check your database, or you can restart any stopped applications after receiving this event.
  3. ContextStoppedEvent: This event is published when the ApplicationContext is stopped using the stop() method in the ConfigurableApplicationContext interface. You can perform necessary cleanup work after receiving this event.
  4. ContextClosedEvent: This event is published when the ApplicationContext is closed using the close() method in the ConfigurableApplicationContext interface. A closed context reaches the end of its lifecycle; it cannot be refreshed or restarted.
  5. RequestHandledEvent: This is a web-specific event that informs all beans that an HTTP request has been serviced. It can only be applied to web applications using DispatcherServlet. When Spring handles user requests as a front-end MVC controller, the system automatically triggers this event after processing the request.

Now that we understand these built-in events, we can listen for ContextRefreshedEvent to perform some operations when Spring Boot starts, as shown in the code below:

@Component
public class TestApplicationListener implements ApplicationListener<ContextRefreshedEvent>{
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        System.out.println(contextRefreshedEvent);
        System.out.println("TestApplicationListener............................");
    }
}

Advanced Play#

You can customize events to fulfill specific requirements, such as performing some business processing after an email is sent successfully.

  1. Custom EmailEvent, code as follows:

    public class EmailEvent extends ApplicationEvent{
         private String address;
         private String text;
         public EmailEvent(Object source, String address, String text){
             super(source);
             this.address = address;
             this.text = text;
         }
         public EmailEvent(Object source) {
             super(source);
         }
         //......setter and getter for address and text
    }
    
  2. Custom listener, code as follows:

    public class EmailNotifier implements ApplicationListener{
         public void onApplicationEvent(ApplicationEvent event) {
             if (event instanceof EmailEvent) {
                 EmailEvent emailEvent = (EmailEvent)event;
                 System.out.println("Email address: " + emailEvent.getAddress());
                 System.out.println("Email content: " + emailEvent.getText());
             } else {
                 System.out.println("Container itself event: " + event);
             }
         }
    }
    
  3. After sending the email, trigger the event, code as follows:

    public class SpringTest {
         public static void main(String args[]){
             ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
             // Create an ApplicationEvent object
             EmailEvent event = new EmailEvent("hello","[email protected]","This is a test");
             // Actively trigger the event
             context.publishEvent(event);
         }
    }
    

2. SpringBoot's CommandLineRunner Interface#

When the container initialization is complete, the run() method in CommandLineRunner will be called, which can also achieve some tasks after the container starts. This method is more flexible compared to ApplicationListener, as follows:

  • Different implementations of CommandLineRunner can specify execution order using @Order()
  • It can receive parameters input from the console.

Below is a custom implementation class, code as follows:

@Component
@Slf4j
public class CustomCommandLineRunner implements CommandLineRunner {

    /**
     * @param args Receives parameters passed from the console
     */
    @Override
    public void run(String... args) throws Exception {
        log.debug("Parameters received from console>>>>"+ Arrays.asList(args));
    }
}

Run this jar with the following command:

java -jar demo.jar aaa bbb ccc

In the above command, three parameters are passed: aaa, bbb, and ccc, which will be received by the run() method. As shown in the figure below:

image

Source Code Analysis#

Those who have read my articles should know how CommandLineRunner is executed. Original text: Bald Head Series, Twenty-Three Pictures to Analyze the Spring Boot Startup Process from Source Code~

The entry point for Spring Boot to load the context is in the org.springframework.context.ConfigurableApplicationContext() method, as shown in the figure below:

image

The CommandLineRunner is executed in the callRunners(context, applicationArguments); method, as shown in the source code below:

image

3. SpringBoot's ApplicationRunner Interface#

ApplicationRunner and CommandLineRunner are both provided by Spring Boot. Compared to CommandLineRunner, it has better encapsulation for parameters passed from the console, allowing you to retrieve specified parameters through key-value pairs, such as --version=2.1.0.

At this point, run this jar with the following command:

java -jar demo.jar --version=2.1.0 aaa bbb ccc

In the above command, four parameters are passed: one key-value pair version=2.1.0, and the other three are aaa, bbb, and ccc.

You can also specify priority using @Order(), as shown in the code below:

@Component
@Slf4j
public class CustomApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.debug("Parameters received from console: {},{},{}",args.getOptionNames(),args.getNonOptionArgs(),args.getSourceArgs());
    }
}

Running with the above command yields the following result:

image

Source Code Analysis#

Like CommandLineRunner, it is also executed in the callRunners() method, as shown in the source code below:

image

4. @PostConstruct Annotation#

The first three methods are for performing some tasks after the container initialization is complete, while the @PostConstruct annotation is for performing tasks after the Bean initialization is complete, such as registering some listeners...

The @PostConstruct annotation is generally placed on a method of the Bean, and once the Bean initialization is complete, this method will be called, as shown in the code below:

@Component
@Slf4j
public class SimpleExampleBean {

    @PostConstruct
    public void init(){
        log.debug("Bean initialization complete, calling...........");
    }
}

5. Specifying Initialization Method in @Bean Annotation#

This method is similar to @PostConstruct, also specifying a method to be called after the Bean initialization is complete.

Create a new Bean, code as follows:

@Slf4j
public class SimpleExampleBean {

    public void init(){
        log.debug("Bean initialization complete, calling...........");
    }
}

In the configuration class, instantiate this Bean using @Bean, but the initMethod attribute in @Bean needs to specify the method to be executed after initialization, as shown below:

@Bean(initMethod = "init")
public SimpleExampleBean simpleExampleBean(){
    return new SimpleExampleBean();
}

6. InitializingBean Interface#

The usage of InitializingBean is basically the same as @PostConstruct, except that the corresponding Bean needs to implement the afterPropertiesSet method, as shown in the code below:

@Slf4j
@Component
public class SimpleExampleBean implements InitializingBean {

    @Override
    public void afterPropertiesSet()  {
        log.debug("Bean initialization complete, calling...........");
    }
}

Conclusion#

There are many implementation solutions, and the author has only summarized six commonly used ones. If you learned something, please give a thumbs up.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.