skip to content

What is the difference between CommandLineRunner and ApplicationRunner?

level: middleimportance: must knowfreq 60%

answer

  1. same lifecycle, different arg type
  2. CommandLineRunner = raw String...
  3. ApplicationRunner = parsed ApplicationArguments
  4. options vs non-options
  5. ordered together, not per-type

basics

~10 s

Both run once at startup. CommandLineRunner.run receives the raw String[] arguments. ApplicationRunner.run receives an ApplicationArguments object that has already parsed those strings into --option flags and plain (non-option) arguments.

solid answer

~40 s

They are functionally identical in lifecycle — both are startup callbacks invoked by SpringApplication after the context is ready, and both can be ordered together with @Order. The only real difference is the run() signature. CommandLineRunner.run(String... args) hands you the raw, unparsed command-line strings, so you interpret them yourself. ApplicationRunner.run(ApplicationArguments args) hands you a richer abstraction that has already split the arguments into 'option' arguments (those in --name or --name=value form) and 'non-option' arguments (everything else), exposing methods like getOptionNames(), getOptionValues(name), containsOption(name), and getNonOptionArgs(). Prefer ApplicationRunner when your startup logic needs to branch on named flags, because you avoid hand-rolling argument parsing. Choose CommandLineRunner for the simplest cases where you just want the raw tokens or don't care about arguments at all.

code

java · 19 lines
java
// ApplicationRunner: parsed access to flags
@Component
class ImportRunner implements ApplicationRunner {
    public void run(ApplicationArguments args) {
        if (args.containsOption("import")) {
            List<String> files = args.getOptionValues("import"); // --import=a.csv --import=b.csv
            files.forEach(this::load);
        }
    }
    void load(String f) { /* ... */ }
}

// CommandLineRunner: raw tokens
@Component
class RawRunner implements CommandLineRunner {
    public void run(String... args) {
        for (String a : args) System.out.println(a);
    }
}

go deeper

for a junior

State that the only difference is the argument type: raw strings vs a parsed object.

for a middle

List the ApplicationArguments accessor methods and note they are ordered together across both types.

for a senior

Advise which to pick based on whether you branch on named flags, and clarify there is no built-in ordering between the two interfaces.

for a principal

Discuss designing CLI-style Spring Boot apps where option flags drive startup behavior and how ordering interacts with data-dependency between runners.

## The one meaningful difference: the argument type Both interfaces are startup callbacks with identical timing and semantics. What differs is what their `run()` method receives: - `CommandLineRunner.run(String... args)` — the **raw** array of command-line tokens, exactly as they appeared after the main class. Nothing is parsed. If the app was launched with `--server.port=9000 import /data/file.csv`, you get the array `["--server.port=9000", "import", "/data/file.csv"]` and must parse it yourself. - `ApplicationRunner.run(ApplicationArguments args)` — a Spring abstraction (`org.springframework.boot.ApplicationArguments`) that has **already parsed** those same tokens into two categories: - **Option arguments**: tokens in `--name` or `--name=value` form. Accessible via `getOptionNames()` (a `Set<String>`), `getOptionValues(String name)` (a `List<String>`, or `null` if the option is absent), and `containsOption(String name)`. - **Non-option arguments**: everything that does not start with `--`. Accessible via `getNonOptionArgs()` (a `List<String>`). - The original raw array is still available via `getSourceArgs()`. ## They share everything else - Same lifecycle position (after context refresh, before `ApplicationReadyEvent`). - Same one-shot, synchronous, main-thread execution. - Same failure semantics (a thrown exception aborts startup). - **Crucially, they are ordered together.** Spring collects all `ApplicationRunner` and `CommandLineRunner` beans into one list and sorts by `@Order`/`Ordered`, so ordering is across both types, not per-type. ## Which to choose - Use **`ApplicationRunner`** when your startup work depends on named flags — e.g. `--seed`, `--reindex`, `--tenant=acme` — because you get parsing for free and clearer code. - Use **`CommandLineRunner`** when you either ignore arguments entirely or genuinely want the raw strings (e.g. passing them straight to another library's parser). ## Common misconception Candidates sometimes claim one runs before the other by design. There is no such rule; relative order between an `ApplicationRunner` and a `CommandLineRunner` is determined solely by their `@Order` values (default order is `Ordered.LOWEST_PRECEDENCE`).

  • If you declare both a CommandLineRunner and an ApplicationRunner, which runs first?
    Neither by default — both default to LOWEST_PRECEDENCE. Spring merges both interface types into one list and sorts by @Order/Ordered, so you control relative order explicitly regardless of type.
  • Can a single bean implement both interfaces?
    Yes, but it is unusual; both run() methods would each be invoked once. It is clearer to implement just the one whose argument type you need.

saying these in an interview costs you the question

  • Saying ApplicationRunner always runs before CommandLineRunner (or vice versa)
  • Claiming CommandLineRunner also parses --options for you
  • Believing the two interfaces have different lifecycle timing

context