4.2 KiB
4.2 KiB
startup-runners
Companion project for the article CommandLineRunner vs ApplicationRunner vs ApplicationReadyEvent in Spring Boot 4 on 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
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() |