Intermediate Reading time: ~8 min

Lifecycle

SpringApplication, startup process, ApplicationRunner, CommandLineRunner

Lifecycle

1. Definition

The Spring Boot application lifecycle is the process from the SpringApplication.run() call to application shutdown. This includes creating the ApplicationContext, auto-configuration, bean initialization, starting the embedded server, and graceful shutdown.

Spring Boot provides numerous extension points (hooks) at various lifecycle phases:

  • ApplicationRunner / CommandLineRunner — logic after startup
  • ApplicationEvent system — event-driven integration
  • SmartLifecycle — fine-grained start/stop ordering
  • @PreDestroy / DisposableBean — shutdown logic
@SpringBootApplication
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}

2. Core Concepts

SpringApplication startup process

SpringApplication.run() executes the following steps:

  1. Create SpringApplication instance — detect application type (Servlet, Reactive, None)
  2. Notify SpringApplicationRunListeners — starting event
  3. Prepare Environment — load property sources, activate profiles
  4. Print Banner — the Spring Boot banner
  5. Create ApplicationContext — according to the context type
  6. Context refresh — load bean definitions, auto-configuration, bean initialization
  7. Run Runners — ApplicationRunner, CommandLineRunner
  8. Done — started → running event

ApplicationRunner vs CommandLineRunner

@Component
public class MyAppRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) {
        // ApplicationArguments — structured argument access
        boolean debug = args.containsOption("debug");
        List<String> files = args.getNonOptionArgs();
    }
}

@Component
public class MyCmdRunner implements CommandLineRunner {
    @Override
    public void run(String... args) {
        // Raw String array — simple arguments
        System.out.println("Arguments: " + Arrays.toString(args));
    }
}

Application Events

Event When?
ApplicationStartingEvent Beginning of SpringApplication.run()
ApplicationEnvironmentPreparedEvent Environment ready, context not yet created
ApplicationContextInitializedEvent Context created, beans not yet loaded
ApplicationPreparedEvent Bean definitions loaded, before refresh
ApplicationStartedEvent Context refresh done, before runners
ApplicationReadyEvent Runners completed, application ready
ApplicationFailedEvent Error occurred during startup

Shutdown

Spring Boot supports graceful shutdown:

# Graceful shutdown
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30s

3. Practical Usage

Post-startup initialization

@Component
@Order(1) // order: lower = earlier
public class DatabaseInitializer implements ApplicationRunner {
    private final DataSource dataSource;

    public DatabaseInitializer(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void run(ApplicationArguments args) {
        // Database schema initialization
        // The context is fully initialized
    }
}

@Component
@Order(2)
public class CacheWarmUp implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) {
        // Cache warm-up after database initialization
    }
}

Event listeners

@Component
public class StartupListener {

    @EventListener(ApplicationReadyEvent.class)
    public void onReady() {
        // Application is fully ready
        System.out.println("Application is ready!");
    }

    @EventListener(ApplicationStartedEvent.class)
    public void onStarted() {
        // Context refresh done, runners haven't executed yet
    }
}

Early event listeners (spring.factories)

ApplicationStartingEvent and ApplicationEnvironmentPreparedEvent occur before context creation, so they cannot be registered as @Component:

public class EarlyEventListener implements ApplicationListener<ApplicationStartingEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartingEvent event) {
        System.out.println("Application starting...");
    }
}

Registration:

# META-INF/spring.factories
org.springframework.context.ApplicationListener=\
  com.example.EarlyEventListener

Customizing SpringApplication

public class MyApp {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(MyApp.class);
        app.setBannerMode(Banner.Mode.OFF);
        app.setAdditionalProfiles("metrics");
        app.setDefaultProperties(Map.of("server.port", "9090"));
        app.run(args);
    }
}

4. Code Examples

SmartLifecycle implementation

@Component
public class MessageConsumer implements SmartLifecycle {
    private volatile boolean running = false;

    @Override
    public void start() {
        running = true;
        // Start Kafka/RabbitMQ consumer
    }

    @Override
    public void stop() {
        running = false;
        // Stop consumer
    }

    @Override
    public boolean isRunning() {
        return running;
    }

    @Override
    public int getPhase() {
        return Integer.MAX_VALUE; // starts last, stops first
    }

    @Override
    public boolean isAutoStartup() {
        return true;
    }
}

Bean lifecycle annotations

@Component
public class ConnectionPool {
    private HikariDataSource dataSource;

    @PostConstruct
    public void init() {
        // After bean creation — initialization
        dataSource = new HikariDataSource();
        dataSource.setJdbcUrl("jdbc:postgresql://localhost/mydb");
    }

    @PreDestroy
    public void cleanup() {
        // On application shutdown — resource cleanup
        if (dataSource != null) {
            dataSource.close();
        }
    }
}

SpringApplicationBuilder (fluent API)

public class MyApp {
    public static void main(String[] args) {
        new SpringApplicationBuilder(MyApp.class)
            .bannerMode(Banner.Mode.LOG)
            .profiles("production")
            .properties("server.port=8443")
            .listeners(new EarlyEventListener())
            .run(args);
    }
}

Startup actuator (Boot 3.x)

management.endpoint.startup.enabled=true
management.endpoints.web.exposure.include=startup
@SpringBootApplication
public class MyApp {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(MyApp.class);
        app.setApplicationStartup(new BufferingApplicationStartup(10000));
        app.run(args);
    }
}

5. Trade-offs

Aspect ApplicationRunner CommandLineRunner
Arguments ApplicationArguments (structured) String[] (raw)
Option parsing Built-in (--key=value) Manual parsing
Use case Complex arguments Simple scripts
Aspect @PostConstruct ApplicationRunner
Execution time During bean creation After full context ready
Other beans Not guaranteed all ready All initialized
Scope Single bean Application-level
Aspect @EventListener ApplicationListener
Registration @Component (in context) spring.factories (early)
Early events ❌ Not accessible ✅ Accessible
Simplicity ✅ Annotation-based ❌ Interface implementation

6. Common Mistakes

❌ Relying on other beans in @PostConstruct

@Component
public class ReportService {
    @Autowired
    private UserRepository userRepository;

    @PostConstruct
    public void init() {
        // RISKY: UserRepository bean is initialized,
        // but the full context (e.g., transaction manager) may not be
        long count = userRepository.count();
    }
}

✅ Use ApplicationRunner instead

@Component
public class ReportService implements ApplicationRunner {
    private final UserRepository userRepository;

    public ReportService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public void run(ApplicationArguments args) {
        // SAFE: the full context is initialized
        long count = userRepository.count();
    }
}

❌ Early event as @Component

// WRONG: ApplicationStartingEvent occurs BEFORE context creation
@Component // ← will NOT receive the event!
public class MyListener implements ApplicationListener<ApplicationStartingEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartingEvent event) { }
}

✅ Register in spring.factories

# META-INF/spring.factories
org.springframework.context.ApplicationListener=\
  com.example.MyListener

❌ Missing runner ordering

When multiple Runners depend on each other but lack @Order → random execution order.

@Component
@Order(1) // ← ALWAYS set order when dependencies exist
public class FirstRunner implements ApplicationRunner { ... }

@Component
@Order(2)
public class SecondRunner implements ApplicationRunner { ... }

7. Deep Dive

Complete startup sequence

1. main() calls SpringApplication.run()
2. SpringApplication instance created
3. SpringApplicationRunListeners.starting()
4. Environment preparation (property sources, profiles)
5. Banner printing
6. ApplicationContext creation (type: Servlet/Reactive/None)
7. Context refresh:
   a. BeanFactory preparation
   b. BeanDefinition loading
   c. BeanFactoryPostProcessor execution
   d. Bean instantiation + dependency injection
   e. BeanPostProcessor (AOP proxy, @PostConstruct)
   f. SmartLifecycle.start()
8. ApplicationStartedEvent
9. ApplicationRunner / CommandLineRunner execution
10. ApplicationReadyEvent
11. Application running (accepting requests)

Graceful shutdown details

server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30s

Graceful shutdown process:

  1. Stop accepting new requests
  2. Wait for in-flight requests (up to timeout)
  3. SmartLifecycle.stop() — reverse phase order
  4. @PreDestroy / DisposableBean.destroy()
  5. ApplicationContext close

Startup performance measurement

@SpringBootApplication
public class MyApp {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(MyApp.class);
        // Boot 3.x: measure startup steps
        app.setApplicationStartup(new BufferingApplicationStartup(10000));
        app.run(args);
    }
}
// GET /actuator/startup → JSON with detailed timing

Liveness and Readiness probes

# Kubernetes probes
management.endpoint.health.probes.enabled=true
management.health.livenessState.enabled=true
management.health.readinessState.enabled=true
  • Liveness: is the application running (not deadlocked)
  • Readiness: is it ready to accept requests (runners completed)

8. Interview Questions

  1. What is the difference between ApplicationRunner and CommandLineRunner? ApplicationRunner: ApplicationArguments (structured, option parsing). CommandLineRunner: String[] (raw). Both run after full context initialization.

  2. What is the Spring Boot startup order? Environment → Banner → Context create → Refresh (beans) → Started event → Runners → Ready event.

  3. What is the difference between @PostConstruct and ApplicationRunner? @PostConstruct: runs during individual bean creation (not guaranteed all other beans are ready). ApplicationRunner: runs after the full context is initialized.

  4. How do you listen to early events? Register the ApplicationListener in spring.factories, because events before context creation are not accessible via @Component.

  5. What is graceful shutdown? Stop accepting new requests, wait for in-flight ones, SmartLifecycle.stop(), @PreDestroy, context close.

  6. What is SmartLifecycle? Interface for fine-grained start/stop ordering. The phase value determines the order. Lower phase = starts earlier, stops later.

  7. How do you measure startup performance? BufferingApplicationStartup + /actuator/startup endpoint. Boot 3.x feature.

9. Glossary

Term Meaning
SpringApplication Spring Boot application entry point and bootstrap class
ApplicationRunner Post-startup logic execution (ApplicationArguments)
CommandLineRunner Post-startup logic execution (String[])
ApplicationEvent Application lifecycle events
ApplicationReadyEvent Application fully ready (runners completed)
SmartLifecycle Fine-grained start/stop ordering interface
@PostConstruct Post-bean-creation initialization
@PreDestroy Pre-shutdown cleanup
Graceful shutdown Completing requests before shutdown
BufferingApplicationStartup Startup performance measurer (Boot 3.x)

10. Cheatsheet

Startup order:
  main() → SpringApplication.run()
  → Environment → Banner → Context create
  → Refresh (beans) → SmartLifecycle.start()
  → ApplicationStartedEvent
  → ApplicationRunner / CommandLineRunner
  → ApplicationReadyEvent → application running

Shutdown order:
  SIGTERM → stop accepting new requests
  → wait for in-flight requests
  → SmartLifecycle.stop() (reverse phase)
  → @PreDestroy / DisposableBean
  → Context close
// ApplicationRunner (structured arguments)
@Component @Order(1)
public class MyRunner implements ApplicationRunner {
    public void run(ApplicationArguments args) { }
}

// CommandLineRunner (raw String[])
@Component @Order(2)
public class MyCmdRunner implements CommandLineRunner {
    public void run(String... args) { }
}

// Event listener
@EventListener(ApplicationReadyEvent.class)
public void onReady() { }

// SmartLifecycle (start/stop ordering)
implements SmartLifecycle { getPhase(); start(); stop(); }
# Graceful shutdown
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30s

# Kubernetes probes
management.endpoint.health.probes.enabled=true
management.health.livenessState.enabled=true
management.health.readinessState.enabled=true

# Startup performance measurement
management.endpoint.startup.enabled=true

🎼 Games

10 questions