ApplicationRunner, InitializingBean, @PostConstruct execution sequence

Overview

There may be scenarios in development where you need to execute something when the container starts. For example, reading configuration files, database connections, etc. SpringBoot provides us with two interfaces to help us achieve this requirement. The two startup loading interfaces are: CommandLineRunner and ApplicationRunner. Spring provides the interface InitializingBean, and jdk provides @PostConstruct

The difference between CommandLineRunner and ApplicationRunner

The functions of CommandLineRunner and ApplicationRunner are the same. The difference is that the run() method of the CommandLineRunner interface receives a String array as a parameter, which is the most original parameter, without any processing; while the run() method of the ApplicationRunner interface receives an ApplicationArguments object as a parameter, which further refines the original parameters. of packaging.

When the program starts, the parameters we pass to the main() method can be accessed by the run() method of the class that implements the CommandLineRunner and ApplicationRunner interfaces, which can receive the parameters passed when starting the service. We can create multiple classes that implement the CommandLineRunner and ApplicationRunner interfaces. In order to make them execute in a certain order, you can use the @Order annotation or implement the Ordered interface.

Example of ApplicationRunner interface

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
@Order(value = 1)
public class JDDRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {

        System.out.println("This is to test the ApplicationRunner interface");
               String strArgs = Arrays.stream(arg0.getSourceArgs()).collect(Collectors.joining("|"));
        System.out.println("Application started with arguments:" + strArgs);
    }
}

Specify parameters when starting: java -jar xxxx.jar data1 data2 data3

This is to test the ApplicationRunner interface

Application started with arguments:data1|data2|data3

CommandLineRunner interface example

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class TestCommandLineRunner implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        System.out.println("This is to test the CommandLineRunn interface");
         String strArgs = Arrays.stream(args).collect(Collectors.joining("|"));
        System.out.println("Application started with arguments:" + strArgs);
    }
}

Specify parameters when starting: java -jar xxxx.jar data1 data2 data3

operation result:

This is to test the CommandLineRunn interface

Application started with arguments:data1|data2|data3

Execution order of CommandLineRunner and ApplicationRunner

In the spring boot program, we can use more than one bean that implements CommandLineRunner and ApplicationRunner. In order to execute the run() method of these beans in order, you can use the @Order annotation or the Ordered interface.

@Component
@Order(2)
public class ApplicationRunnerBean1 implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments arg0) throws Exception {
        System.out.println("ApplicationRunnerBean 1");
    }
}
@Component
@Order(4)
public class ApplicationRunnerBean2 implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments arg0) throws Exception {
        System.out.println("ApplicationRunnerBean 2");
    }
}
@Component
@Order(1)
public class CommandLineRunnerBean1 implements CommandLineRunner {
    @Override
    public void run(String... args) {
        System.out.println("CommandLineRunnerBean 1");
    }
}
@Component
@Order(3)
public class CommandLineRunnerBean2 implements CommandLineRunner {
    @Override
    public void run(String... args) {
        System.out.println("CommandLineRunnerBean 2");
    }
}

Results of the:

CommandLineRunnerBean 1 ApplicationRunnerBean 1 CommandLineRunnerBean 2 ApplicationRunnerBean 2

Some interfaces are not executed when implementing multiple ApplicationRunner

@Component
@Slf4j
public class RunnerTest1 implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {

        while (true) {
            System.out.println("this is RunnerTest1");
            Thread.sleep(100);
        }

    }
}
@Component
@Slf4j
public class RunnerTest2 implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {

        while (true) {
            System.out.println("this is RunnerTest2");
            Thread.sleep(100);
        }

    }
}

Only the methods in RunnerTest1 will be executed.

By analyzing the source code of springboot startup, we can find that after the applicationContext container is loaded, the callRunners method of the SpringApplication class will be called:

This method will obtain all interface beans that implement ApplicationRunner and CommandLineRunner, and then execute the corresponding run methods in sequence, and execute them in the same thread. Therefore, if the run method of a bean that implements the ApplicationRunner interface keeps looping without returning, subsequent code will not be executed.

ApplicationRunner, InitializingBean, @PostConstruct execution order issues

Usage of InitializingBean interface

The InitializingBean interface provides an initialization method for beans. It only includes the afterPropertiesSet method. All classes that inherit this interface will execute this method when initializing the bean. Note that it is best to add Spring annotation injection to implement this interface, such as @Component

Usage of @PostConstruct annotation

If you want to complete certain initialization operations when generating an object, but these initialization operations rely on dependency injection, then you cannot implement it in the constructor. For this purpose, you can use @PostConstruct to annotate a method to complete initialization. The method annotated with @PostConstruct will be automatically called after dependency injection is completed. Priority: Constructor >> @Autowired >> @PostConstruct

@Component
public class Test implements InitializingBean, ApplicationRunner, CommandLineRunner {

    @PostConstruct
    public void init(){
        System.out.println("PostConstruct method execution");
    }


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

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("This is to test the ApplicationRunner interface");

    }

    @Override
    public void run(String... args) throws Exception {
        System.out.println("This is to test the CommandLineRunn interface");
    }
}

PostConstruct method execution InitializingBean method execution

This is to test the ApplicationRunner interface. This is to test the CommandLineRunn interface.

It can be seen from this: @PostConstruct>InitializingBean>ApplicationRunner>CommandLineRunner