03. Execute during springboot startup: @ PostConstruct, CommandLineRunner, ApplicationRunner, ApplicationListener

Execute when springboot starts: CommandLineRunner, ApplicationRunner, ApplicationListener @PostConstruct comparison

Usage scenario:

During the development process, we will have such a scenario: we need to perform some operations after the project is started, such as reading configuration file information, database connection, deleting temporary files, clearing cache information, factory class initialization, loading active data, or cache synchronization. We have many implementation methods, such as @ PostConstruct, CommandLineRunner, ApplicationRunner and ApplicationListener. We can implement our specific logic after springboot is started. Next, compare their differences

1 @PostConstruct

This annotation is used to modify a non static void method. The method modified by @ PostConstruct will run when the server loads the Servlet and will only be executed by the server once.

Trigger timing:

SpringBoot will automatically initialize the global single instance of the class or interface marked with Bean related annotations (such as @ Component, @ Service, @ Repository, etc.). If marked, the initialization order will be in the order marked by the user, otherwise it will be initialized in the default order. During initialization, after executing the construction method of a Bean, the @ PostConstruct method (if any) of the Bean will be executed, and then the next Bean will be initialized.
bean creation process in spring

Configure beans (@ component, @ Service, @ Controller and other annotation configurations) ---- > resolve to Bean metadata (BeanDefinition object in Bean container) ---- > generate beans according to Bean metadata (create beans)

Execution order when creating bean s

Constructor - > @ Autowired - > @ postconstruct (annotated method)

Example:
@PostConstruct
public void dispatcher() throws Exception {
    // Logic code
}
advantage:
  • It's easy to use. Just add this annotation to the class managed by the spring container
Disadvantages:
  • It is triggered when spring creates beans. At this time, the container has not been fully initialized. If the logic references beans that have not been initialized, an exception will be caused. Therefore, the loading order needs to be considered.
  • If the logic processing time in the @ PostConstruct method is long, it will increase the time for the SpringBoot application to initialize the Bean, and then increase the time for the application to start. Because the SpringBoot application will open the port to provide services only after the Bean initialization is completed, the application cannot be accessed before that.
  • Bottom line: it will affect the time when your program starts.

2 ,CommandLineRunner,ApplicationRunner

It is very simple to use. You only need to implement the CommandLineRunner or ApplicationRunner interface and rewrite the run method.

Trigger timing:

Start the source code through springboot:

After startup, the callRunners method will be executed;

public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   //Set thread start timer
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   //Configure system properties: default: the external display screen is missing and can be started
   configureHeadlessProperty();
   //Get and start the event listener. If there are no other listeners in the project, there is only EventPublishingRunListener by default
   SpringApplicationRunListeners listeners = getRunListeners(args);
   //Broadcast events to listeners
   listeners.starting();
   try {
       //For implementing the ApplicationRunner interface, the user sets the ApplicationArguments parameter for encapsulation
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      //Configure the running environment: for example, activate the application * * * yml profile      
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
      configureIgnoreBeanInfo(environment);
      //Load the configured banner(gif,txt...), Console drawing
      Banner printedBanner = printBanner(environment);
      //Create a context object and instantiate it
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      //Configuring the SPring container      
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
      //Refresh the Spring context and create the bean      
      refreshContext(context);
      //Empty method, subclass implementation
      afterRefresh(context, applicationArguments);
      //Stop timer: calculates the thread start time
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
      //Stop event listener
      listeners.started(context);
      //Start loading resources
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, listeners, exceptionReporters, ex);
      throw new IllegalStateException(ex);
   }
   listeners.running(context);
   return context;
}

callRunners method:

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    //Store the classes that implement the ApplicationRunner and CommandLineRunner interfaces into the collection
   List<Object> runners = new ArrayList<>();
   runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
   runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
   //Sort by loading order
   AnnotationAwareOrderComparator.sort(runners);
   for (Object runner : new LinkedHashSet<>(runners)) {
      if (runner instanceof ApplicationRunner) {
         callRunner((ApplicationRunner) runner, args);
      }
      if (runner instanceof CommandLineRunner) {
         callRunner((CommandLineRunner) runner, args);
      }
   }
}

As can be seen from the above source code, after the springboot is fully initialized, CommandLineRunner and ApplicationRunner will be executed. The only difference between the two is that the parameters are different, but they will not affect. The execution parameters can be obtained.

Example
/**
 * @author
 * @date 2021-08-23 16:19
 */
@Component
public class ServerDispatcher implements CommandLineRunner {
    @Override
    public void run(String... args){
        // Logic code
    }
}
/**
 * @author
 * @date 2021-08-23 16:19
 */
@Component
public class ServerDispatcher2 implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args){
        // Logic code
    }
}

3,ApplicationListener

Through event listening, we can also implement the springboot startup execution method. Implement ApplicationListener and override onApplicationEvent method to execute after all bean s are loaded.

Trigger timing:

During the startup process of the IOC container, when all bean s have been processed, the spring ioc container will have an action to publish the ContextRefreshedEvent event.

Example
/**
 * @author
 * @date 2021-08-23 16:19
 */
@Component
public class ServerDispatcher3 implements ApplicationListener<ContextRefreshedEvent> {
    
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        // Logic code
    }
}
be careful:

There will be two containers in the system, one is root application context, and the other is our own ProjectName servlet context (as a sub container of root application context)

  • ApplicationContext
    • context.pushevent()
  • WebtapplicationContext
    • context.pushevent()

In this case, the onApplicationEvent method will be executed twice. To avoid the problems mentioned above, we can call logic code only after initialization of root application context, and do not deal with other container initialization.

  //root application context has no parent
 if (event.getApplicationContext().getParent() == null) { 
    //Logic code
  }

summary

  1. Some independent and compact initialization logic will not affect the startup speed of springboot. Use @ PostConstruct annotation;
  2. If you want to listen through the ApplicationListener event, you need to handle the specified container.
  3. At the data initialization level, @ PostConstruct and ApplicationListener are not recommended because both will affect the startup of the program. If the execution of logic takes a long time, it will take a long time to start the service.
  4. I suggest using CommandLineRunner and ApplicationRunner, which will not affect the startup speed of the service and is relatively simple to handle.

Keywords: Java Spring Spring Boot

Added by redhair on Mon, 24 Jan 2022 03:22:56 +0200