skip to content

What is SpringApplicationRunListener, how is it registered, and how does it relate to the lifecycle events?

level: seniorimportance: should knowfreq 28%

answer

  1. SPI backbone of run()
  2. callbacks: starting/environmentPrepared/contextPrepared/contextLoaded/started/ready/failed
  3. EventPublishingRunListener emits the events
  4. spring.factories SpringApplicationRunListener key
  5. constructor (SpringApplication, String[])

basics

~10 s

SpringApplicationRunListener is the low-level hook Spring Boot calls during the run sequence (starting, environmentPrepared, contextPrepared, contextLoaded, started, ready, failed). Boot's built-in one publishes the lifecycle events. You register custom ones via META-INF/spring.factories.

solid answer

~40 s

SpringApplicationRunListener is an SPI interface with callbacks for each phase of SpringApplication.run: starting, environmentPrepared, contextPrepared, contextLoaded, started, ready, and failed. Boot ships EventPublishingRunListener, which implements these callbacks by publishing the corresponding application events (ApplicationStartingEvent, ApplicationEnvironmentPreparedEvent, ApplicationContextInitializedEvent, ApplicationPreparedEvent, ApplicationStartedEvent, ApplicationReadyEvent, ApplicationFailedEvent) through an event multicaster. So the events you listen to are literally produced by a run listener. Custom SpringApplicationRunListeners are registered in META-INF/spring.factories under the org.springframework.boot.SpringApplicationRunListener key; Boot instantiates each via a constructor taking (SpringApplication, String[] args). They run before the context exists, so they're for framework-level bootstrap concerns — most application code should just listen to the events instead of implementing this interface.

code

java · 20 lines
java
// META-INF/spring.factories
// org.springframework.boot.SpringApplicationRunListener=com.example.TimingRunListener

package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import java.time.Duration;

public class TimingRunListener implements SpringApplicationRunListener {
    // MANDATORY constructor signature — Boot invokes this reflectively
    public TimingRunListener(SpringApplication app, String[] args) { }

    @Override public void starting(org.springframework.boot.ConfigurableBootstrapContext ctx) {
        System.out.println("run starting");
    }
    @Override public void ready(ConfigurableApplicationContext ctx, Duration timeTaken) {
        System.out.println("ready in " + timeTaken.toMillis() + "ms");
    }
}

go deeper

for a junior

Just know Boot has an internal hook that fires the lifecycle events.

for a middle

Name the phase callbacks and that they map to the events.

for a senior

Explain EventPublishingRunListener, the spring.factories key, and the mandatory constructor; know when to prefer events instead.

for a principal

Reason about run-listener use for framework instrumentation, bootstrap context, ordering, and the run-listener-vs-event abstraction boundary.

## What it is `SpringApplicationRunListener` (package `org.springframework.boot`) is a **service-provider interface (SPI)** — an extension point Spring Boot invokes at each stage of `SpringApplication.run(...)`. Think of it as the callback backbone of the startup pipeline. It is lower-level than application events. ## The callbacks (mapped to events) For each run listener, Boot calls, in order: - `starting(ConfigurableBootstrapContext)` → publishes **ApplicationStartingEvent** - `environmentPrepared(bootstrapContext, ConfigurableEnvironment)` → **ApplicationEnvironmentPreparedEvent** - `contextPrepared(ConfigurableApplicationContext)` → **ApplicationContextInitializedEvent** - `contextLoaded(ConfigurableApplicationContext)` → **ApplicationPreparedEvent** - `started(context, timeTaken)` → **ApplicationStartedEvent** - `ready(context, timeTaken)` → **ApplicationReadyEvent** - `failed(context, throwable)` → **ApplicationFailedEvent** (Exact signatures vary slightly by Boot version; the phase set is stable.) ## The built-in implementation Boot's default run listener is **`EventPublishingRunListener`**. Its whole job is to translate these callbacks into **published application events** using a `SimpleApplicationEventMulticaster`. This is *why* application-lifecycle events exist at all — a run listener emits them. So the two concepts are the same machinery at different levels: run listener = the caller, events = the broadcast. ## How you register a custom one List the implementation class in **`META-INF/spring.factories`**: ``` org.springframework.boot.SpringApplicationRunListener=\ com.example.MyRunListener ``` Boot loads it during bootstrap and instantiates it reflectively via a **constructor accepting `(SpringApplication application, String[] args)`** — that specific constructor is mandatory. Because instantiation happens before the context, a run listener cannot use dependency injection. ## When to use it vs. just listening to events - **Prefer listening to the events** (`ApplicationListener` / `@EventListener`) for almost all application needs — it's simpler and better documented. - Implement `SpringApplicationRunListener` only for **framework-level** concerns: instrumenting the entire run, custom bootstrap contexts, or integrating a tracing/startup-metrics system that needs every phase boundary. Libraries and Boot itself use it (e.g. startup timing, `BackgroundPreinitializer` interplay). ## Gotchas - **Wrong key**: `SpringApplicationRunListener` and `ApplicationListener` are **different** spring.factories keys; mixing them means your class is never loaded or is loaded for the wrong purpose. - **Missing constructor**: without the `(SpringApplication, String[])` constructor, Boot fails to instantiate the run listener. - **No DI**: don't expect beans; you get the `SpringApplication`, args, and later the `ConfigurableApplicationContext` handed in. - Exceptions thrown in a run listener callback can abort startup and drive the `failed(...)` path. ## Relationship summary Events are the ergonomic, public-facing API; `SpringApplicationRunListener` is the internal engine that fires them. Understanding this explains *why* early events can't be caught by beans (the run listener multicasts to listeners seeded before the context) and where to hook if you need phase-level control the events don't expose.

  • Which built-in SpringApplicationRunListener actually publishes the lifecycle events?
    EventPublishingRunListener. Its callbacks broadcast ApplicationStartingEvent, ApplicationEnvironmentPreparedEvent, etc., through an event multicaster — that's the source of all the Boot lifecycle events.
  • What constructor must a custom SpringApplicationRunListener declare?
    One taking (SpringApplication application, String[] args). Boot instantiates the run listener reflectively with that signature during bootstrap; without it, instantiation fails.

saying these in an interview costs you the question

  • Saying run listeners are registered as @Component beans
  • Using the ApplicationListener key for a run listener (or vice versa)
  • Claiming you can @Autowire dependencies into a run listener
  • Thinking events and run listeners are unrelated mechanisms

context