skip to content

Walk through what happens when refresh() is called on an ApplicationContext.

level: seniorimportance: must knowfreq 60%

answer

  1. prepare → obtainFactory → prepareFactory → invoke BFPP → register BPP
  2. messageSource → multicaster → onRefresh → listeners
  3. finishBeanFactoryInit = preInstantiateSingletons (eager)
  4. finishRefresh = start Lifecycle + ContextRefreshedEvent
  5. failure → destroyBeans + cancelRefresh

basics

~10 s

refresh() is the startup routine: it prepares and loads the bean factory, applies configuration post-processors, registers post-processors, sets up the message source, event multicaster, and listeners, eagerly creates all non-lazy singletons, then publishes ContextRefreshedEvent.

solid answer

~30 s

refresh() (in AbstractApplicationContext) drives the whole container startup as an ordered, synchronized sequence: prepareRefresh (mark active, init property sources), obtainFreshBeanFactory (load/create the DefaultListableBeanFactory and its bean definitions), prepareBeanFactory (register standard beans/resolvers), invokeBeanFactoryPostProcessors (this is where @Configuration classes are parsed by ConfigurationClassPostProcessor and definitions can be added/modified), registerBeanPostProcessors, initMessageSource, initApplicationEventMulticaster, onRefresh (subclass hook — Boot starts the embedded web server here), registerListeners, finishBeanFactoryInitialization (preInstantiateSingletons — eagerly builds all non-lazy singletons), and finishRefresh (start Lifecycle beans, publish ContextRefreshedEvent). If any step throws, it destroys already-created singletons and resets the active flag before rethrowing.

code

java · 10 lines
java
// A ContextRefreshedEvent listener runs after finishRefresh() — all
// singletons exist and Lifecycle beans have started.
@Component
class ReadyLogger {
    @EventListener
    void onReady(ContextRefreshedEvent e) {
        // safe: every non-lazy singleton is fully initialized here
        System.out.println("context ready: " + e.getApplicationContext().getId());
    }
}

go deeper

for a junior

Know that refresh() is the startup that builds all beans and that ContextRefreshedEvent signals completion.

for a middle

List the major phases in order and identify that non-lazy singletons are pre-instantiated near the end.

for a senior

Reproduce the full ordered sequence, explain why BFPPs precede singleton creation, and describe failure cleanup.

for a principal

Reason about extension points (onRefresh for web servers, ordering of post-processors), refresh-once vs refreshable semantics, and how this pipeline underpins Boot startup and graceful shutdown.

`refresh()` is defined on `AbstractApplicationContext` and is `synchronized` on a startup monitor. It runs this fixed sequence — memorize the ordering, it explains most 'why did my bean init in that order' questions: 1. **prepareRefresh()** — record start time, set the `active` flag, initialize property sources (`initPropertySources()`), and validate required properties (`Environment.validateRequiredProperties`). 2. **obtainFreshBeanFactory()** — get the `ConfigurableListableBeanFactory`. For `AbstractRefreshableApplicationContext` (e.g. `ClassPathXmlApplicationContext`) this *destroys any prior factory, creates a fresh `DefaultListableBeanFactory`, and loads bean definitions* (parses XML / scans). For `GenericApplicationContext` it just returns the already-populated factory and flips a one-time `refreshed` guard. 3. **prepareBeanFactory()** — configure the factory: set the bean classloader, register the SpEL expression resolver and a `ResourceEditorRegistrar`, add `ApplicationContextAwareProcessor`, ignore certain aware interfaces for autowiring, and register resolvable dependencies (`BeanFactory`, `ApplicationContext`, `Environment`, etc.) plus default environment beans. 4. **postProcessBeanFactory()** — empty subclass hook. 5. **invokeBeanFactoryPostProcessors()** — instantiate and invoke all `BeanFactoryPostProcessor`s in order (`PriorityOrdered` → `Ordered` → rest). Crucially this includes `ConfigurationClassPostProcessor`, which parses `@Configuration`/`@Bean`/`@ComponentScan` and *registers the resulting bean definitions*. After this step the full set of bean definitions is known. (BFPP mechanics themselves are a sibling topic.) 6. **registerBeanPostProcessors()** — instantiate and *register* (not yet invoke) all `BeanPostProcessor`s so they can wrap beans created later. 7. **initMessageSource()** — set up i18n `MessageSource` (falls back to a delegating default if none defined). 8. **initApplicationEventMulticaster()** — create the `ApplicationEventMulticaster` used to dispatch events. 9. **onRefresh()** — subclass hook. In Spring Boot's web contexts this is where the *embedded Tomcat/Netty server is created* (though it starts accepting requests in finishRefresh). 10. **registerListeners()** — register statically declared `ApplicationListener`s and any listener beans; earlier-published early events are flushed now. 11. **finishBeanFactoryInitialization()** — freeze the configuration and call `beanFactory.preInstantiateSingletons()`, which **eagerly instantiates every non-lazy singleton** (this is when most of your `@Component`/`@Bean` objects are actually created and wired). Beans marked `@Lazy` are skipped until first use. 12. **finishRefresh()** — clear resource caches, initialize the `LifecycleProcessor`, call `getLifecycleProcessor().onRefresh()` (which **starts `SmartLifecycle` beans with `isAutoStartup()`** — phased start belongs to the lifecycle sibling topic), and publish `ContextRefreshedEvent`. **Failure handling.** The whole body is wrapped in try/catch: on exception it logs, calls `destroyBeans()` to run destroy callbacks on already-created singletons, sets `active=false` via `cancelRefresh()`, and rethrows. A partially built context is thus torn down cleanly. **close() is the mirror image** — `doClose()` publishes `ContextClosedEvent`, stops `Lifecycle` beans, calls `destroySingletons()` (running `@PreDestroy`/`DisposableBean`), and closes the bean factory. `registerShutdownHook()` wires that to JVM shutdown. **Gotchas:** (a) because non-lazy singletons are created in step 11, a broken bean fails startup *fast* — that's intentional. (b) `ContextRefreshedEvent` fires at the very end, so it's the safe place to run code that needs all beans ready. (c) Anything that adds/modifies bean definitions must run as a BeanFactoryPostProcessor (step 5) — after step 11 the definitions are frozen.

  • At which phase are your @Component singletons actually instantiated?
    In finishBeanFactoryInitialization(), via beanFactory.preInstantiateSingletons() — the second-to-last step. Non-lazy singletons are created eagerly there; @Lazy ones are deferred.
  • Why must a BeanFactoryPostProcessor run before finishBeanFactoryInitialization?
    BFPPs (step 5) are the only place bean *definitions* can be added or mutated. After preInstantiateSingletons the definitions are frozen and singletons are being built, so definition changes would be too late.
  • What does refresh() do if a singleton fails to construct?
    The refresh body catches the exception, calls destroyBeans() to run destroy callbacks on already-created singletons, cancels the refresh (active=false), and rethrows — so you don't get a half-started container.

saying these in an interview costs you the question

  • Saying singletons are created lazily on first getBean by default (they're eagerly created during refresh)
  • Placing embedded-server startup or ContextRefreshedEvent at the wrong end of the sequence
  • Claiming you can add bean definitions after refresh completes
  • Confusing invokeBeanFactoryPostProcessors (definitions) with registerBeanPostProcessors (bean wrapping)

context