Files
spring-boot-demo/startup-runners/README.md
T

69 lines
4.2 KiB
Markdown

# startup-runners
Companion project for the article **[CommandLineRunner vs ApplicationRunner vs ApplicationReadyEvent in Spring Boot 4](https://ankurm.com/commandlinerunner-vs-applicationrunner-vs-applicationreadyevent-spring-boot-4/)** on **[ankurm.com](https://ankurm.com)**.
Every console block quoted in the article came out of `output/`. Transcripts 00-14 are written by the test
suite (so a claim that stops being true turns the build red); 15-17 are read out of the Spring Boot and Spring
Framework sources and jars by `scripts/capture-facts.sh`.
There is deliberately **no `docs/` folder**: the deeper material lives in collapsible sections inside the article.
## Versions
| | |
|---|---|
| Spring Boot | 4.1.2 (GA 2026-10-22 on Maven Central) |
| Spring Framework | 7.0.10 |
| JDK | 25 (25.0.3) |
| Maven | 3.9 |
| Sources read for contrast | Spring Boot 3.0.13 and 3.1.12 (`callRunners` only, not run) |
## Quickstart
```bash
export JAVA_HOME=/path/to/jdk-25
mvn test # runs the scenarios and rewrites output/00-14
./scripts/run-all.sh # everything, including 15-17 (needs network access to Maven Central once)
```
Transcript 03 (four runners without `@Order`) is the one file that is allowed to differ between runs: that is its point.
## Source layout
Each scenario is its own package with its own `@SpringBootApplication`.
| Package | What it holds |
|---|---|
| `hello/` | the smallest example: a `CommandLineRunner`, an `ApplicationRunner`, an `ApplicationReadyEvent` listener |
| `order/` | every startup hook plus `@PostConstruct`, `InitializingBean`, `SmartLifecycle`, printing the sequence and the readiness state |
| `ordering/`, `ties/` | `@Order` on classes, `@Bean` methods and `Ordered`; four runners with no order at all |
| `args/` | the same command line seen as `String[]` and as `ApplicationArguments` |
| `failure/` | a runner that throws, an `ApplicationReadyEvent` listener that throws, `ExitCodeGenerator`, a checked exception (profiles) |
| `blocking/` | a runner that blocks (web app, actuator probes) and one that hands its work to a virtual thread |
| `lifecycle/`, `lifestartfails/`, `lifeforgetful/` | `SmartLifecycle` phases, a `start()` that throws, an `isRunning()` that never says true |
| `slices/` | startup hooks declared four ways, for the `@SpringBootTest` and `@WebMvcTest` scenarios |
| `support/` | `Trace`, the in-memory log the tests print |
## Captured output
| File | What it shows |
|---|---|
| `00-hello.txt` | the three hooks of `HelloApp`, started with a real command line |
| `01-startup-hooks-in-order.txt` | every hook, in order, non-web and web, with the readiness state each one sees |
| `02-order-annotation-and-ordered.txt` | both runner kinds sorted together by `@Order` / `Ordered`; a bean that is both kinds |
| `03-ties-have-no-promised-order.txt` | four unordered runners in 8 fresh JVMs |
| `04-arguments.txt` | `String[]` versus `ApplicationArguments` |
| `05-runner-failure.txt` | a throwing runner: events, log, exit codes (1, 42), checked-exception wrapping |
| `06-ready-listener-failure.txt` | a throwing `ApplicationReadyEvent` listener, non-web and web |
| `07-blocking-runner-readiness.txt` | a blocking runner: port open, liveness UP, readiness 503 |
| `08-async-runner.txt` | a runner that returns at once and a background task that fails |
| `09-smartlifecycle-phases.txt` | start in ascending phase, stop in descending phase, async stop, a bean that is not auto-started |
| `10-lifecycle-beans-in-a-web-app.txt` | every `SmartLifecycle` bean in a web app with its phase |
| `11-smartlifecycle-start-fails.txt` | `start()` throws: no started event, no runner |
| `12-smartlifecycle-forgot-isrunning.txt` | `isRunning()` always false: `stop()` never called |
| `13-spring-boot-test-runs-the-hooks.txt` | what `@SpringBootTest` runs |
| `14-webmvctest-slice-hooks.txt` | what `@WebMvcTest` runs |
| `15-callrunners-source.txt` | `SpringApplication.run`, `callRunners`, `callRunner` and the run listener, from the 4.1.2 sources, plus 3.0.13 and 3.1.12 |
| `16-javap-callrunners.txt` | the compiled `callRunners` and the runner interfaces, read with `javap` |
| `17-lifecycleprocessor-source.txt` | when `DefaultLifecycleProcessor` calls `start()` and `stop()` |