What does spring.main.lazy-initialization=true do, and what are its trade-offs?
answer
- all beans created on first use
- faster startup, deferred failures
- fail-fast lost → errors at first request
- LazyInitializationExcludeFilter / @Lazy(false)
- good for dev/tests, risky for prod
basics
~10 sIt makes every bean lazy: beans are created the first time they're needed instead of at startup. Startup gets faster, but startup-time errors and slow bean creation are deferred to first use.
solid answer
~40 s`spring.main.lazy-initialization=true` flips the whole `ApplicationContext` to lazy mode: bean instances are created on first access rather than eagerly during context refresh. The main benefit is **faster startup** (great for dev, tests, and functions/CLIs that touch few beans). The costs: initialization failures that would normally fail-fast at startup now surface later — potentially on the first HTTP request; the first request that triggers a cold bean is slower; and JMX/metrics for un-created beans are missing. You can selectively opt beans back into eager creation with `@Lazy(false)` on the bean or a `LazyInitializationExcludeFilter` bean (Spring Boot registers one for things like scheduled/JMX beans). For production it's usually better to keep eager init so misconfigurations blow up at deploy time.
code
java · 17 lines// application.properties
// spring.main.lazy-initialization=true
// Force one bean to stay eager despite the global flag:
@Configuration
class WarmupConfig {
@Bean
static LazyInitializationExcludeFilter keepWarmersEager() {
return LazyInitializationExcludeFilter.forBeanTypes(CacheWarmer.class);
}
// ...or annotate the specific bean:
@Bean
@Lazy(false)
CacheWarmer cacheWarmer() { return new CacheWarmer(); }
}go deeper
Know it defers bean creation to first use and speeds startup.
Articulate the fail-fast trade-off and first-request latency; know how to enable it.
Know LazyInitializationExcludeFilter / @Lazy(false) opt-outs and when prod should avoid it.
Frame it against alternatives (targeted @Lazy, AOT/native) and reason about failure-mode economics: deploy-time vs runtime failure.
### What lazy initialization means Normally when the Spring `ApplicationContext` **refreshes**, it eagerly instantiates all non-lazy singleton beans (`AbstractApplicationContext.finishBeanFactoryInitialization` → `preInstantiateSingletons`). With **lazy initialization** turned on, singletons are marked lazy so they are only instantiated the **first time they are requested** — either injected into another bean that's being created, or fetched from the context. ### Turning it on - Global: `spring.main.lazy-initialization=true` (property) — Spring Boot applies a `LazyInitializationBeanFactoryPostProcessor` that sets `lazyInit=true` on every eligible bean definition. - Programmatic: `new SpringApplicationBuilder(App.class).lazyInitialization(true).run(args)` or `SpringApplication.setLazyInitialization(true)`. - Per bean (independent of the global flag): the classic `@Lazy` annotation on a `@Bean` method or component. ### Benefits - **Faster startup** — only the beans actually used get created. Big win for: local development, integration tests that exercise a slice, short-lived CLI apps, and serverless/function workloads. - Lower memory footprint when many beans are never touched. ### Costs / gotchas - **Deferred failures**: a misconfigured bean (bad datasource URL, missing property, failing `@PostConstruct`) normally fails the context at startup (**fail-fast**). Lazy init pushes that failure to the moment the bean is first used — possibly a user's first request in production. This is the single biggest reason to avoid it in prod. - **First-request latency**: the request that triggers creation of a cold dependency graph pays the construction cost (connection pools, caches, etc.). - **Missing eager side effects**: beans whose whole purpose is a startup side effect (warming caches, registering listeners, JMX exposure, metrics binders) won't run until touched. Actuator/metrics for un-created beans are absent. ### Opting specific beans out of laziness Even with the global flag on you can force eager creation: - `@Lazy(false)` on the bean definition. - Register a `org.springframework.boot.LazyInitializationExcludeFilter` bean. Spring Boot itself registers filters so that, e.g., `@Scheduled`/`@JmsListener`/config-props stay eager. Example: ```java @Bean static LazyInitializationExcludeFilter eagerCaches() { return LazyInitializationExcludeFilter.forBeanTypes(CacheWarmer.class); } ``` ### When to use - **Yes**: dev/test startup speed, small tools, environments where you deliberately trade fail-fast for speed. - **No (usually)**: production services where you want configuration errors to break the deployment, not the first customer request. A better production alternative for slow-start problems is targeted `@Lazy` on genuinely heavy, rarely-used beans plus AOT/native images. ### Relationship to `@Lazy` The global flag is essentially applying `@Lazy` everywhere. `@Lazy` also has a subtler use: on an injection point it injects a lazy **proxy**, breaking eager dependency ordering (and sometimes circular dependencies).
- Your app starts fine with lazy init but 500s on the first request in prod. Why?A bean with a broken configuration was never created at startup because it was lazy, so the fail-fast check didn't run. The first request triggered its creation, which failed. With eager init it would have failed the deployment instead.
- How do you keep only a few beans eager while lazy-initializing the rest?Annotate them with @Lazy(false) or register a LazyInitializationExcludeFilter (e.g. forBeanTypes(...)). Spring Boot uses such filters internally to keep scheduling/JMX beans eager.
saying these in an interview costs you the question
- Claiming lazy init makes the running app faster overall (it only shifts cost, and adds first-hit latency)
- Saying it's safe to always enable in production
- Thinking @PostConstruct / cache-warming still runs at startup under lazy init
- Confusing spring.main.lazy-initialization with just adding @Lazy to one bean