Skip to content
Spring Boot sb core 3 min read

CommandLineRunner & ApplicationRunner

Sometimes you need code to run once at startup, after the application context is fully initialized but as part of the boot sequence — seeding a database, warming a cache, or printing diagnostics. Spring Boot provides two functional interfaces for exactly this: CommandLineRunner and ApplicationRunner. Any bean implementing either runs automatically just before SpringApplication.run(...) returns.

Why runners?

You could put startup logic in a @PostConstruct method, but that fires while the owning bean is still being created — before the rest of the context is guaranteed ready and before the embedded web server starts. Runners execute after the context is fully refreshed and the server is up, which is the correct moment for “the app is ready, now do X” tasks. They also receive the program’s command-line arguments, which @PostConstruct cannot.

CommandLineRunner

CommandLineRunner hands you the raw arguments as a String[], exactly as the JVM received them.

@Component
public class StartupRunner implements CommandLineRunner {

    @Override
    public void run(String... args) {
        System.out.println("CommandLineRunner started with " + args.length + " args");
        for (String arg : args) {
            System.out.println("  arg = " + arg);
        }
    }
}

ApplicationRunner

ApplicationRunner receives an ApplicationArguments object that has already parsed the raw input into option arguments (--name=value) and non-option arguments (everything else).

@Component
public class DataSeeder implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) {
        System.out.println("Option names: " + args.getOptionNames());
        System.out.println("--seed values: " + args.getOptionValues("seed"));
        System.out.println("Non-option args: " + args.getNonOptionArgs());
        System.out.println("Has --verbose? " + args.containsOption("verbose"));
    }
}

Running the app like this:

java -jar app.jar --seed=true import.csv --verbose

Output:

Option names: [seed, verbose]
--seed values: [true]
Non-option args: [import.csv]
Has --verbose? true

getOptionValues returns a List<String> because the same option may appear more than once (--seed=a --seed=b); it returns null if the option is absent.

CommandLineRunner vs ApplicationRunner

Both run at the same point in the lifecycle. The only real difference is the shape of the argument they receive.

AspectCommandLineRunnerApplicationRunner
Method signaturerun(String... args)run(ApplicationArguments args)
Argument formRaw String[], unparsedParsed into options + non-options
Option accessManual parsinggetOptionNames(), getOptionValues(name)
Non-option accessManual parsinggetNonOptionArgs()
Best forSimple, few or no argsApps that read --flag=value style args

Tip: Reach for ApplicationRunner whenever you accept named flags — it saves you from writing fragile string-splitting code.

Ordering multiple runners

When several runners exist, control their sequence with @Order (or by implementing Ordered). Lower numbers run first. Runners without @Order are treated as the lowest precedence and run last.

@Component
@Order(1)
public class FirstRunner implements CommandLineRunner {
    public void run(String... args) {
        System.out.println("First runner");
    }
}

@Component
@Order(2)
public class SecondRunner implements ApplicationRunner {
    public void run(ApplicationArguments args) {
        System.out.println("Second runner");
    }
}

Output:

First runner
Second runner

Note: CommandLineRunner and ApplicationRunner beans share a single ordered list, so an @Order(1) ApplicationRunner still runs before an @Order(2) CommandLineRunner.

Lambda style

Because both are functional interfaces, you can declare a runner concisely as a @Bean instead of a full class — useful for small one-off tasks.

@Configuration
public class StartupConfig {

    @Bean
    CommandLineRunner welcome() {
        return args -> System.out.println("Application started successfully");
    }
}

Error handling

If a runner throws an exception, SpringApplication.run(...) propagates it and the application fails to start (the JVM exits with a non-zero code). That is usually the desired behavior — you do not want a half-initialized app serving traffic. Wrap recoverable work in try/catch if a failure should not abort startup.

Best Practices

  • Use runners for startup-only tasks; for recurring work see scheduling instead.
  • Prefer ApplicationRunner when parsing named command-line options.
  • Keep runner logic fast; long-running work delays the app becoming ready.
  • Make ordering explicit with @Order when more than one runner exists.
  • Let unrecoverable startup failures propagate so the app fails fast.
Last updated June 13, 2026
Was this helpful?