skip to content

Explain eager singleton pre-instantiation in ApplicationContext versus lazy lookup in BeanFactory. Why does it matter?

level: middleimportance: must knowfreq 70%

answer

  1. refresh() → preInstantiateSingletons()
  2. Eager = fail fast at startup
  3. @Lazy defers one bean or a group
  4. spring.main.lazy-initialization flips whole context
  5. Prototypes/FactoryBean products still on demand

basics

~20 s

A plain BeanFactory creates a bean only when you first ask for it (lazy). An ApplicationContext creates all non-lazy singleton beans up front at startup (eager), so configuration mistakes fail immediately instead of later at runtime.

solid answer

~40 s

In a raw BeanFactory, singletons are instantiated lazily — the first `getBean` call triggers creation. ApplicationContext instead pre-instantiates all singleton beans that aren't marked `@Lazy` during `refresh()`, specifically in `finishBeanFactoryInitialization` → `preInstantiateSingletons()`. The benefit is fail-fast: if a required dependency is missing, a `@Value` can't resolve, or a constructor throws, you find out at application startup, not on the first user request in production. It also means singletons are warm and ready, avoiding first-request latency. You can opt individual beans out with `@Lazy`, or opt an entire `@Configuration`/`@ComponentScan` into lazy mode. FactoryBeans and prototype-scoped beans are still created on demand. Boot embraces eager startup by default, though `spring.main.lazy-initialization=true` flips the whole context to lazy.

code

java · 16 lines
java
@Configuration
class AppConfig {

    @Bean
    ExpensiveClient expensiveClient() { // eager by default
        return new ExpensiveClient(); // built at startup -> errors surface now
    }

    @Bean
    @Lazy // deferred: created only on first actual use
    ReportGenerator reportGenerator(ExpensiveClient c) {
        return new ReportGenerator(c);
    }
}
// application.properties:
// spring.main.lazy-initialization=true  # flips the ENTIRE context to lazy

go deeper

for a junior

Say ApplicationContext builds singletons at startup while a raw BeanFactory builds them on first request, and eager startup catches errors early.

for a middle

Name preInstantiateSingletons(), the fail-fast benefit, and how @Lazy / spring.main.lazy-initialization change behavior.

for a senior

Place it in the refresh() lifecycle, discuss cold-start vs fail-fast trade-offs, prototypes/FactoryBean exceptions, and when targeted laziness is justified.

for a principal

Weigh startup-time vs safety at platform scale, discuss lazy-proxy interplay with circular deps, ordering of post-processors vs regular singletons, and policy for lazy-init in different environments.

## Bean scopes and instantiation timing A **singleton** bean (the default scope) has exactly one instance per container. The question of *when* that single instance gets built distinguishes the two containers. ### Lazy in a raw BeanFactory `DefaultListableBeanFactory` used directly does nothing until you call `getBean`. Each singleton is built on first request and then cached. This minimizes startup work and memory if you only ever touch a few beans — useful in constrained or tooling scenarios. ### Eager in ApplicationContext When you construct an ApplicationContext, its `refresh()` method runs a fixed sequence. Near the end, `finishBeanFactoryInitialization(beanFactory)` calls `beanFactory.preInstantiateSingletons()`. That loops over every registered `BeanDefinition` and, for each one that is a **singleton, non-abstract, and non-lazy**, calls `getBean(name)` — forcing full instantiation, dependency injection, post-processing, and `@PostConstruct` right there at startup. ### Why eager matters — fail fast The biggest win is **early error detection**: - Missing required dependency → `NoSuchBeanDefinitionException` at startup. - Ambiguous autowiring → `NoUniqueBeanDefinitionException` at startup. - Unresolvable `@Value("${...}")` placeholder → failure at startup. - Exception in a constructor or `@PostConstruct` → startup fails loudly. With lazy loading these same errors could stay hidden until a specific code path first needs the bean — potentially in production, mid-request. Eager startup surfaces them during deploy/boot. Secondary benefit: **no cold-start latency** — the first user request doesn't pay the cost of building the object graph, running AOP proxying, opening pools, etc. ### Opting out — @Lazy - `@Lazy` on a `@Bean` method or `@Component` defers *that* bean until first use. - `@Lazy` on a `@Configuration` or with `@ComponentScan(lazyInit=true)` defers a group. - Spring Boot: `spring.main.lazy-initialization=true` makes the *entire* context lazy. Trade-off: faster startup, but you lose fail-fast and pay first-request latency. Handy for large dev-time apps, risky for prod because misconfig surfaces late. ### Things that are always on demand - **Prototype** beans: never pre-instantiated; a new instance is created on each `getBean`/injection point. - **FactoryBean**: the FactoryBean itself may be pre-instantiated, but the *product* (`getObject()`) is typically produced lazily unless it's a `SmartFactoryBean` reporting eager init. - Beans behind lazy proxies (`@Lazy` injection points) — a proxy is injected; the real bean is built on first method call. ### Gotchas - Marking too much `@Lazy` can mask configuration errors and move failures into runtime request paths. - `@Lazy` + circular dependencies: laziness (via proxy) is sometimes used to break a constructor-injection cycle, but that's a design smell. - Eager init interacts with `@DependsOn` and `@Order` for infrastructure beans; post-processors are instantiated *before* regular singletons regardless. ### When to use which Keep eager (default) in production for fail-fast safety. Use targeted `@Lazy` for expensive, rarely-used beans (e.g., a heavyweight client only some code paths need). Use context-wide lazy init mainly to speed up local dev startup.

  • Which refresh() step performs eager instantiation, and which method name?
    finishBeanFactoryInitialization(beanFactory), which calls beanFactory.preInstantiateSingletons() to build all non-lazy singleton definitions.
  • What's the downside of spring.main.lazy-initialization=true in production?
    You lose fail-fast: configuration errors (missing beans, bad placeholders, ambiguous autowiring) no longer surface at startup but on first use, possibly mid-request. You also pay cold-start latency on those first requests.

saying these in an interview costs you the question

  • Saying ApplicationContext is lazy — it eagerly pre-instantiates non-lazy singletons.
  • Claiming @Lazy affects prototype beans (prototypes are already created on demand).
  • Believing eager init instantiates prototype and lazy beans too.

context