Backend Development 7 min read

Master SpringBoot Runners: ApplicationRunner vs CommandLineRunner Explained

This guide explains how to run custom code after SpringApplication starts by implementing ApplicationRunner or CommandLineRunner, compares their method signatures and argument handling, shows execution timing, provides practical examples, and demonstrates how to control execution order with @Order or Ordered.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master SpringBoot Runners: ApplicationRunner vs CommandLineRunner Explained

Environment: SpringBoot 3.3.0

1. Introduction

If you need to execute specific code after SpringApplication starts, implement the ApplicationRunner or CommandLineRunner interface. Both provide a run method that is invoked before SpringApplication.run(...) completes.

2. Differences Between the Two

ApplicationRunner method signature

<code>void run(ApplicationArguments args) throws Exception;</code>

CommandLineRunner method signature

<code>void run(String... args) throws Exception;</code>

The only difference is the parameter type. Below are example usages.

First, add startup parameters, e.g.:

<code>--pack.title=xxxooo --pack.version=1.0.1</code>

ApplicationRunner example

<code>public void run(ApplicationArguments args) throws Exception {
  System.out.printf("AR Args: %s%n", Arrays.toString(args.getSourceArgs()));
  System.out.printf("AR 参数名: %s%n", args.getOptionNames());
}</code>

Output:

CommandLineRunner example

<code>public void run(String... args) throws Exception {
  System.err.printf("CLR Args: %s%n", Arrays.toString(args));
}</code>

Output:

Compared to CommandLineRunner, ApplicationRunner provides richer argument handling, including access to original arguments and option names via getOptionNames() and getOptionValues() .

3. Execution Timing

When SpringApplication#run is called, the core method eventually invokes the runners:

<code>public class SpringApplication {
  public ConfigurableApplicationContext run(String... args) {
    // ...
    try {
      prepareContext(...);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      // ...
      // Call ApplicationRunner and CommandLineRunner
      callRunners(context, applicationArguments);
    }
    // ...
    return context;
  }
}</code>

The runners are invoked in the following order:

<code>private void callRunner(Runner runner, ApplicationArguments args) {
  if (runner instanceof ApplicationRunner) {
    callRunner(ApplicationRunner.class, runner, applicationRunner -> applicationRunner.run(args));
  }
  if (runner instanceof CommandLineRunner) {
    callRunner(CommandLineRunner.class, runner, commandLineRunner -> commandLineRunner.run(args.getSourceArgs()));
  }
}</code>

Thus, ApplicationRunner runs first .

4. Practical Examples

Example 1 – Schedule a Task After Startup

<code>@Component
public class TaskSchedulerInitializer implements ApplicationRunner {
  private final TaskScheduler taskScheduler;
  public TaskSchedulerInitializer(TaskScheduler taskScheduler) {
    this.taskScheduler = taskScheduler;
  }
  @Override
  public void run(ApplicationArguments args) throws Exception {
    taskScheduler.schedule(this::task, new CronTrigger("0/5 * * * * ?"));
  }
  private void task() {
    System.out.println("执行任务...");
  }
}</code>

Note: You need to configure a TaskScheduler bean.

Example 2 – Warm Up Cache After Container Starts

<code>@Component
public class CacheDataWarmup implements ApplicationRunner {
  private final CacheManager cacheManager;
  private final SysDictService dictService;
  public CacheDataWarmup(CacheManager cacheManager, SysDictService dictService) {
    this.cacheManager = cacheManager;
    this.dictService = dictService;
  }
  @Override
  public void run(ApplicationArguments args) throws Exception {
    List<SysDict> dicts = this.dictService.queryAll();
    this.cacheManager.getCache("dicts").put("allparams", dicts);
  }
}</code>

Ensure the cache starter is on the classpath, or implement your own cache.

Example 3 – Load External Resource Files

<code>@Component
public class LoadOuterResource implements ApplicationRunner {
  private final ConfigurableEnvironment environment;
  public LoadOuterResource(ConfigurableEnvironment environment) {
    this.environment = environment;
  }
  @Override
  public void run(ApplicationArguments args) throws Exception {
    YamlPropertySourceLoader sourceLoader = new YamlPropertySourceLoader();
    try {
      List<PropertySource<?>> list = sourceLoader.load("pack", new ClassPathResource("com/pack/binder/properties/pack.yml"));
      list.forEach(propertySource -> environment.getPropertySources().addLast(propertySource));
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}</code>

These examples demonstrate various tasks you can perform with runners.

5. Controlling Execution Order

If multiple runners exist and a specific order is required, implement org.springframework.core.Ordered or use the @Order annotation.

<code>@Component
@Order(2)
public class FirstRunner implements ApplicationRunner {
  // ...
}

@Component
@Order(1)
public class SecondRunner implements ApplicationRunner {
  // ...
}</code>

Execution will be: SecondRunner → FirstRunner .

BackendJavaSpringBootApplicationRunnerRunnerCommandLineRunner
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.