Why can't a @Component @EventListener catch ApplicationStartingEvent or ApplicationEnvironmentPreparedEvent, and how do you listen to those early events?
answer
- @EventListener needs a live context to scan
- early events predate the context
- addListeners / builder.listeners()
- spring.factories ApplicationListener key
- EnvironmentPostProcessor for config mutation
basics
~10 sThose events fire before the ApplicationContext and its beans exist, so a bean-based @EventListener isn't registered yet. You must register an ApplicationListener before the context is built — via SpringApplication.addListeners(), SpringApplicationBuilder.listeners(), or META-INF/spring.factories.
solid answer
~40 sApplicationStartingEvent and ApplicationEnvironmentPreparedEvent are published very early — before the ApplicationContext is created and therefore before any @Component / @EventListener bean exists. @EventListener works by scanning beans in a live context, so at that moment there is nothing to scan; the listener would simply never be invoked. To observe early events you register a plain ApplicationListener that lives outside the context lifecycle. Three ways: (1) programmatically, SpringApplication.addListeners(...) or SpringApplicationBuilder.listeners(...) before calling run; (2) declaratively, list the ApplicationListener class in META-INF/spring.factories under the org.springframework.context.ApplicationListener key so Boot loads it during bootstrap; (3) for configuration mutation specifically, use an EnvironmentPostProcessor (also registered via spring.factories). These listeners are instantiated by Boot itself, not by the container, so they see events that predate the context.
code
java · 16 lines// src/main/resources/META-INF/spring.factories
// org.springframework.context.ApplicationListener=com.example.EarlyBanner
package com.example;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;
// No @Component: Boot instantiates it during bootstrap, before the context exists.
public class EarlyBanner
implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent e) {
String profiles = String.join(",", e.getEnvironment().getActiveProfiles());
System.out.println("Environment ready, profiles=" + profiles);
}
}go deeper
Know that some events are 'too early' for normal listeners.
Name the three registration mechanisms and why @EventListener fails early.
Explain the multicaster seeded at SpringApplication construction and the no-context constraint on early listeners.
Weigh EnvironmentPostProcessor vs early ApplicationListener, ordering, double-registration hazards, and how starters ship early hooks.
## The core reason `@EventListener` is an **annotation processed by a bean post-processor inside a running `ApplicationContext`**. Spring finds `@EventListener` methods by inspecting the beans it manages. That machinery only exists **after** the context is created and its beans are registered. `ApplicationStartingEvent` and `ApplicationEnvironmentPreparedEvent` are published **before the context exists at all** (Starting: before even the Environment; EnvironmentPrepared: Environment built, context not yet). So there is no bean registry to scan, no `@EventListener` method is discovered, and your handler is silently never called. This is a classic 'my listener doesn't fire' trap. ## How early events are actually broadcast During bootstrap, `SpringApplication` creates a `SimpleApplicationEventMulticaster` seeded with the `ApplicationListener`s it knows about **at construction time** — i.e. the ones you registered before/outside the context. `SpringApplicationRunListener` (the `EventPublishingRunListener`) uses that multicaster to fire the early events to exactly those listeners. ## The three registration mechanisms 1. **Programmatic** — before `run`: ```java SpringApplication app = new SpringApplication(App.class); app.addListeners(new MyEarlyListener()); app.run(args); ``` or with the builder: `new SpringApplicationBuilder(App.class).listeners(new MyEarlyListener()).run(args);` 2. **Declarative via `META-INF/spring.factories`** — put a line under the `ApplicationListener` key. Boot loads these during bootstrap, so they're present for early events: ``` org.springframework.context.ApplicationListener=\ com.example.MyEarlyListener ``` This is how starters ship early listeners without touching `main`. 3. **`EnvironmentPostProcessor`** — the idiomatic way to *change* configuration in response to (or at the time of) `ApplicationEnvironmentPreparedEvent`. Registered via `spring.factories` under `org.springframework.boot.env.EnvironmentPostProcessor`. Boot invokes it while preparing the Environment. ## Constraints on early listeners - They **cannot** be `@Autowired` with context beans — no context exists. They must be self-contained (or read from the `Environment` once EnvironmentPrepared has fired). - `spring.factories`-registered listeners must have a no-arg constructor (or a Boot-recognized constructor), because Boot instantiates them reflectively before Spring DI is available. - Ordering among early listeners follows `@Order`/`Ordered`. ## Gotchas - Adding `@Component` to an early listener does nothing for the early phase — component scanning hasn't happened. It may double-register it for later events, causing surprising double handling. - People try `@EventListener(ApplicationStartingEvent.class)` and see it 'work' only because a *later* redelivery or mis-test; in a real boot it will not fire. - Throwing from an early listener can abort startup (and then trigger ApplicationFailedEvent). ## When to use which - Pure early observation/logging → `spring.factories` ApplicationListener or `addListeners`. - Injecting/overriding property sources, decrypting secrets, computing derived config → `EnvironmentPostProcessor`. - Anything needing beans → wait for `ApplicationPreparedEvent`/`ApplicationReadyEvent` and use a normal `@EventListener` bean.
- Can you @Autowire a repository into an early ApplicationListener?No. The context and its beans don't exist when early events fire, so there is nothing to inject. The listener must be self-contained or read only from the Environment (available from EnvironmentPreparedEvent onward).
- If you want to add or override a property source at startup, which mechanism is idiomatic?An EnvironmentPostProcessor registered via META-INF/spring.factories under the EnvironmentPostProcessor key — it runs while the Environment is being prepared and can add PropertySources.
- What happens if you register the same early listener both in spring.factories and via addListeners?It can be invoked twice for the same event, leading to duplicated side effects; register it in exactly one place.
saying these in an interview costs you the question
- Claiming @Component @EventListener catches ApplicationStartingEvent
- Trying to @Autowire beans into an early listener
- Saying early listeners go in @Configuration classes
- Confusing the spring.factories ApplicationListener key with SpringApplicationRunListener key