How to Run Code When a Spring Boot Application Starts
Introduction
Some work has to happen once, when the application starts: loading reference data, warming a cache, checking that a required directory exists, printing a summary of the configuration. Spring Boot offers several hooks for this, and they run at different moments - some before the other beans exist, some after everything is ready. This quick tip shows which one to use, the order in which they run, and what happens when startup code fails.Tested on Spring Boot 4.1.2, Spring Framework 7.0.10 and JDK 25.
Running startup code with an ApplicationRunner
For code that should run once the application has started, declare anApplicationRunner (or CommandLineRunner) bean: Spring Boot calls it after all beans are created, before the application counts as ready, and passes it the command-line arguments. Several runners are ordered with @Order.
The short answer
@Bean
ApplicationRunner loadReferenceData(ReferenceDataService service) {
return args -> service.load();
}
Which startup hook to use
| Hook | When it runs | Use it for | Limit |
|---|---|---|---|
@PostConstruct on a bean method | While that bean is being created | Initializing the bean itself from its own dependencies | Other beans may not exist yet; the bean's own proxies, such as @Transactional, do not apply to this call |
ApplicationRunner / CommandLineRunner bean | After every bean is created, before the ready event | Startup tasks that use other beans; tasks that read arguments | A failure stops the application |
@EventListener(ApplicationReadyEvent.class) | Last, after the runners | Work that needs a fully started application, such as announcing that it is ready | A failure also stops the application |
ApplicationRunner receives ApplicationArguments, parsed into options such as --mode=full and plain arguments, and CommandLineRunner receives the raw String[].
The order of the startup hooks
The example has one hook of each kind. Each prints a line, and two arguments make a runner or the ready listener fail, so that the effect of a failure can be seen too.src/main/resources/application.properties
spring.main.banner-mode=off
logging.level.root=WARN
StartupApp.java
//StartupApp.java
package com.javabrahman.tips.startup;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class StartupApp {
public static void main(String[] args) {
SpringApplication.run(StartupApp.class, args);
System.out.println("main(): SpringApplication.run() has returned");
}
}
CacheWarmer.java
//CacheWarmer.java
package com.javabrahman.tips.startup;
import jakarta.annotation.PostConstruct;
import org.springframework.stereotype.Component;
@Component
public class CacheWarmer {
@PostConstruct
void init() {
System.out.println("1. @PostConstruct - CacheWarmer is initialized; other beans may not exist yet");
}
}
StartupTasks.java
//StartupTasks.java
package com.javabrahman.tips.startup;
import java.util.Arrays;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
@Configuration
public class StartupTasks {
@EventListener(ApplicationStartedEvent.class)
void started() {
System.out.println("2. ApplicationStartedEvent - the context is refreshed; runners have not run yet");
}
@Bean
@Order(1)
ApplicationRunner loadReferenceData() {
return args -> {
System.out.println("3. ApplicationRunner (@Order 1) - option names: " + args.getOptionNames());
if (args.containsOption("fail-runner")) {
throw new IllegalStateException("reference data could not be loaded");
}
};
}
@Bean
@Order(2)
CommandLineRunner printArguments() {
return args -> System.out.println("4. CommandLineRunner (@Order 2) - raw arguments: " + Arrays.toString(args));
}
@EventListener(ApplicationReadyEvent.class)
void ready(ApplicationReadyEvent event) {
System.out.println("5. ApplicationReadyEvent - the application is ready");
if (event.getArgs().length > 0 && event.getArgs()[0].equals("--fail-ready")) {
throw new IllegalStateException("ready listener failed");
}
}
@EventListener(ApplicationFailedEvent.class)
void failed(ApplicationFailedEvent event) {
System.out.println("X. ApplicationFailedEvent - startup failed: " + event.getException().getMessage());
}
}
1. @PostConstruct - CacheWarmer is initialized; other beans may not exist yet 2. ApplicationStartedEvent - the context is refreshed; runners have not run yet 3. ApplicationRunner (@Order 1) - option names: [mode] 4. CommandLineRunner (@Order 2) - raw arguments: [--mode=full, report.txt] 5. ApplicationReadyEvent - the application is ready main(): SpringApplication.run() has returned
1. @PostConstruct - CacheWarmer is initialized; other beans may not exist yet 2. ApplicationStartedEvent - the context is refreshed; runners have not run yet 3. ApplicationRunner (@Order 1) - option names: [fail-runner] X. ApplicationFailedEvent - startup failed: reference data could not be loaded 2026-11-13T23:01:18.813+05:30 ERROR 37856 --- [ main] o.s.boot.SpringApplication : Application run failed java.lang.IllegalStateException: reference data could not be loaded
1. @PostConstruct - CacheWarmer is initialized; other beans may not exist yet 2. ApplicationStartedEvent - the context is refreshed; runners have not run yet 3. ApplicationRunner (@Order 1) - option names: [fail-ready] 4. CommandLineRunner (@Order 2) - raw arguments: [--fail-ready] 5. ApplicationReadyEvent - the application is ready 2026-11-13T23:01:21.918+05:30 ERROR 60600 --- [ main] o.s.boot.SpringApplication : Application run failed java.lang.IllegalStateException: ready listener failed
- The order is always the same:
@PostConstructwhile the beans are created, thenApplicationStartedEvent, the runners in@Ordersequence, andApplicationReadyEventlast - and only then doesSpringApplication.run()return tomain(). - The
ApplicationRunnersaw the optionmode, parsed from--mode=full; theCommandLineRunnersaw both arguments as they were typed, includingreport.txt. - A failing runner - the exception from runner 3 ended the startup: runner 4 and the ready event never ran,
ApplicationFailedEventwas published, Spring Boot logged "Application run failed", andrun()did not return. - A failing ready listener - every runner had run, but the exception still made the run fail with "Application run failed". The
ApplicationFailedEventlistener was not called in this case.
Pitfalls
- Runners run on the main thread, one after the other, and the application is not ready until they return. A slow task delays readiness, which matters when a platform waits for the readiness probe; start long work on another thread, or use
@Async. - An exception in a runner stops the whole application. Catch and log exceptions from tasks that are allowed to fail.
@PostConstructis called on the bean itself, not through its proxy: a@Transactionalmethod called from it runs without a transaction.- Runners also run in tests that start the application context, such as
@SpringBootTest. Guard tasks that must not run there with a profile or a property.
Related Spring Boot tutorials
@PostConstruct and the rest of a bean's lifecycle are covered in bean lifecycle: @PostConstruct, @PreDestroy and BeanPostProcessorClick to read tutorial: Spring Bean Lifecycle: @PostConstruct, @PreDestroy and More, and Spring's events, including the application events above, in application events with @EventListenerClick to read tutorial: Application Events in Spring with @EventListener.Related Spring Boot tutorials
Spring Bean Lifecycle: @PostConstruct, @PreDestroy and MoreClick to read tutorial: Spring Bean Lifecycle: @PostConstruct, @PreDestroy and More Application Events in Spring with @EventListenerClick to read tutorial: Application Events in Spring with @EventListener Your First Spring Boot Application with Spring InitializrClick to read tutorial: Your First Spring Boot Application with Spring Initializr
Spring Bean Lifecycle: @PostConstruct, @PreDestroy and MoreClick to read tutorial: Spring Bean Lifecycle: @PostConstruct, @PreDestroy and More Application Events in Spring with @EventListenerClick to read tutorial: Application Events in Spring with @EventListener Your First Spring Boot Application with Spring InitializrClick to read tutorial: Your First Spring Boot Application with Spring Initializr