What is SmartInitializingSingleton.afterSingletonsInstantiated(), and how does it differ from SmartLifecycle start()?
answer
- fires after ALL eager singletons instantiated
- preInstantiateSingletons() second pass
- one-shot, no stop pair, before start()
- EventListenerMethodProcessor uses it
- lazy/prototype beans don't trigger it
basics
~20 safterSingletonsInstantiated() is a one-time callback fired after ALL non-lazy singletons are created, so a bean can safely act on the whole set of singletons. It runs during singleton pre-instantiation, before SmartLifecycle start(), which comes later at finishRefresh().
solid answer
~40 sSmartInitializingSingleton is a single-method interface; afterSingletonsInstantiated() is invoked by DefaultListableBeanFactory.preInstantiateSingletons() after every non-lazy singleton has been instantiated and initialized. It's the hook for logic that needs the complete singleton set — e.g. scanning all beans of a type and registering them. Each implementing singleton gets the callback once. It differs from SmartLifecycle.start() in timing and purpose: afterSingletonsInstantiated() fires during finishBeanFactoryInitialization, still inside refresh, before the LifecycleProcessor runs; SmartLifecycle.start() fires later in finishRefresh() and represents starting ongoing activity that is also stopped on close. Use SmartInitializingSingleton for one-shot cross-bean wiring/validation after construction; use SmartLifecycle for start/stop of runtime processes. Note it only fires for eagerly instantiated singletons — lazy or prototype beans don't trigger it.
code
java · 17 linesimport org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Component;
@Component
class HandlerRegistry implements SmartInitializingSingleton {
private final ObjectProvider<MessageHandler> handlers;
HandlerRegistry(ObjectProvider<MessageHandler> handlers) { this.handlers = handlers; }
// Runs once, after every eager singleton (incl. all MessageHandlers) exists.
@Override public void afterSingletonsInstantiated() {
handlers.orderedStream().forEach(h -> register(h.type(), h));
}
private void register(String type, MessageHandler h) { /* ... */ }
}
interface MessageHandler { String type(); }go deeper
Know it's a callback after all singletons are created.
Should contrast it with @PostConstruct (whole set vs single bean).
Should place it in preInstantiateSingletons() and order it before SmartLifecycle.start(); cite EventListenerMethodProcessor.
Distinguishes one-shot cross-bean wiring from phased runtime lifecycle and chooses between it, ContextRefreshedEvent, and ApplicationRunner deliberately.
## The gap it fills `@PostConstruct` runs when *one* bean finishes initializing, but at that moment other singletons may not exist yet. Sometimes you need to act **after the entire set of eager singletons is ready** — for example, collect every bean implementing some interface, build a registry, or cross-validate configuration. That's **`org.springframework.beans.factory.SmartInitializingSingleton`**, a single-method interface: ```java void afterSingletonsInstantiated(); ``` ## When it fires `DefaultListableBeanFactory.preInstantiateSingletons()` (called from `AbstractApplicationContext.finishBeanFactoryInitialization()` during `refresh()`) does two passes: 1. Instantiate all non-lazy singletons (running their `@PostConstruct`/`InitializingBean` callbacks). 2. Iterate again; for each singleton that implements `SmartInitializingSingleton`, call **`afterSingletonsInstantiated()`** once. So by the time your callback runs, **every eager singleton already exists and is fully initialized** — you can safely look them up (e.g. via the `ApplicationContext` or `ObjectProvider`). ## How it differs from SmartLifecycle.start() | Aspect | SmartInitializingSingleton.afterSingletonsInstantiated() | SmartLifecycle.start() | |---|---|---| | Purpose | One-shot post-construction wiring/validation across all singletons | Start ongoing activity (threads, servers, listeners) | | Timing in refresh() | During `finishBeanFactoryInitialization()` (singleton pre-instantiation) | Later, in `finishRefresh()` via the LifecycleProcessor | | Paired stop? | No | Yes — `stop()` on close | | Repeatable? | No, fires once | Yes — can be stopped and started again | | Ordering control | No phases | Phased via getPhase() | Crucially, **afterSingletonsInstantiated() runs strictly before any SmartLifecycle.start()**, because singleton instantiation completes before `finishRefresh()`. So a SmartInitializingSingleton can prepare state that the subsequently-started lifecycle beans rely on. ## Where Spring itself uses it Framework infrastructure uses it heavily: `EventListenerMethodProcessor` (which turns `@EventListener` methods into `ApplicationListener`s) implements `SmartInitializingSingleton` so it can scan *all* beans for annotated methods after they all exist. Scheduling and validation processors use similar post-instantiation scans. ## Gotchas - **Only eager singletons trigger it.** `@Lazy` singletons and prototype beans never receive the callback (they aren't part of pre-instantiation). - **It's not a lifecycle stop pair** — there's no matching "before destruction" method; use `@PreDestroy`/`DisposableBean` for teardown. - **Don't do long-running/blocking work here** — it runs on the bootstrap thread and delays context startup; for anything ongoing, use SmartLifecycle instead. - **Runs during refresh, before ContextRefreshedEvent** — if you specifically want a signal after the context is fully refreshed and lifecycle-started, listen for `ContextRefreshedEvent` or use `ApplicationRunner`/`CommandLineRunner` (Boot) instead.
- Does afterSingletonsInstantiated() fire for lazy or prototype beans?No. It's invoked during eager singleton pre-instantiation, so only non-lazy singletons implementing SmartInitializingSingleton receive it; lazy singletons and prototypes never do.
- Which runs first: afterSingletonsInstantiated() or SmartLifecycle.start()?afterSingletonsInstantiated() — it runs during finishBeanFactoryInitialization(), whereas lifecycle start() runs later in finishRefresh() via the LifecycleProcessor.