What is Bean Lifecycle?
Three caches for resolving circular dependencies:
Junior Level
Bean Lifecycle is all the stages of a bean from creation to destruction.
Why know lifecycle: (1) initialize resources at the right moment, (2) understand why @Transactional does not work from @PostConstruct, (3) properly clean up resources, (4) resolve circular dependencies.
Simple analogy: A person’s life: birth -> growing up -> work -> retirement.
Main stages:
1. Creation (constructor)
2. Dependency injection
3. Initialization (@PostConstruct)
4. Working (ready to use)
5. Destruction (@PreDestroy)
In Spring 6 / Boot 3: use
jakarta.annotation.PostConstructinstead ofjavax.annotation.PostConstruct.
@Service
public class MyBean {
public MyBean() {
// 1. Creation
}
@PostConstruct
public void init() {
// 3. Initialization
}
@PreDestroy
public void cleanup() {
// 5. Destruction
}
}
Middle Level
Lifecycle Phases
1. BeanDefinition -> meta-information
2. Instantiation -> constructor
3. Populate Properties -> @Autowired
4. Aware Interfaces -> setBeanName, setBeanFactory
5. @PostConstruct -> initialization
6. Ready -> bean is ready
7. @PreDestroy -> cleanup
Singleton vs Prototype
| Stage | Singleton | Prototype |
|---|---|---|
| Creation | Once | Every time on request |
| Initialization | Yes | Yes |
| Destruction | Yes | NO NOT called! |
// Prototype: you must clean up resources yourself!
PrototypeBean bean = context.getBean(PrototypeBean.class);
try {
// usage
} finally {
bean.cleanup(); // Manually!
}
SmartLifecycle
// For starting/stopping components
@Component
public class MyService implements SmartLifecycle {
public void start() { /* Start after all beans are initialized */ }
public void stop() { /* Stop on shutdown */ }
public boolean isRunning() { return running; }
public int getPhase() { return 0; } // Startup order
}
Senior Level
When NOT to Use @PostConstruct
- Heavy initialization - blocks the entire context startup. Use SmartLifecycle or @EventListener(ContextRefreshedEvent)
- Calling @Transactional methods - proxy does not exist yet, transaction does not work
- Operations with retry - if initialization can fail (no DB connection), use SmartLifecycle with retries
Three-Level Cache and Cycles
Three caches for resolving circular dependencies:
- singletonObjects - ready beans (fully initialized)
- earlySingletonObjects - created but not initialized (for breaking cycles)
- singletonFactories - factories producing early references
DefaultListableBeanFactory caches:
1. singletonObjects -> ready beans
2. earlySingletonObjects -> created but not initialized
3. singletonFactories -> factories for getting early reference
-> Resolves cycles for Field/Setter Injection
-> Constructor Injection cannot use cache 3
Proxy Creation Point
BPP.postProcessAfterInitialization():
-> This is where AOP Proxies are created
-> Bean is replaced by Proxy in container
@PostConstruct is called BEFORE proxy!
-> Calling @Transactional method from @PostConstruct will NOT work
-> Solution: ApplicationListener<ContextRefreshedEvent>
BPP Ordering
// BPP order matters!
@Order(1)
public class FirstBPP implements BeanPostProcessor { }
@Order(2)
public class SecondBPP implements BeanPostProcessor { }
-> FirstBPP runs earlier
Production Experience
Real scenario: @PostConstruct + @Transactional
@PostConstruct
public void init() {
loadData(); // @Transactional -> does NOT work!
}
// Reason: proxy does not exist yet
// Solution:
@EventListener(ContextRefreshedEvent.class)
public void init() {
loadData(); // Now proxy exists -> transaction works
}
Best Practices
- @PostConstruct -> validation, initialization
- @PreDestroy -> resource cleanup
- Prototype -> clean up resources yourself
- SmartLifecycle -> startup order
- @PostConstruct -> proxy does not exist yet!
- BPP order -> manage via @Order
Summary for Senior
- Instantiation != Initialization
- Three-level cache -> cycle resolution
- Proxy created AFTER @PostConstruct
- Prototype -> @PreDestroy NOT called
- SmartLifecycle -> start/stop components
- ContextRefreshedEvent -> when everything is ready
Interview Cheat Sheet
Must know:
- Bean Lifecycle: creation -> dependency injection -> initialization (
@PostConstruct) -> ready -> destruction (@PreDestroy) - Lifecycle is needed for: resource initialization, understanding why
@Transactionaldoes not work from@PostConstruct, resource cleanup, resolving cycles - Singleton: all stages 1-12,
@PreDestroycalled on context shutdown - Prototype: only creation and initialization,
@PreDestroyNOT called - clean up manually - Three-level cache (singletonObjects, earlySingletonObjects, singletonFactories) resolves circular dependencies
- Proxy created AFTER
@PostConstruct- calling@Transactionalmethod from@PostConstructwill not work SmartLifecycle- for starting/stopping components with order (getPhase())- In Spring 6 / Boot 3:
jakarta.annotation.PostConstructinstead ofjavax.annotation
Common follow-up questions:
- Why doesn’t
@Transactionalwork from@PostConstruct? Proxy is created after@PostConstruct, call goes to the original object. - How to solve the transaction problem during initialization?
@EventListener(ContextRefreshedEvent.class)- all proxies already exist. - Why doesn’t Prototype call
@PreDestroy? Spring does not track prototype bean destruction - call cleanup manually. - What is the three-level cache? Cache of ready beans, early references, and factories - resolves cycles for Field/Setter Injection.
Red flags (DO NOT say):
- ”
@PostConstructis the place for heavy initialization” (blocks context startup, useSmartLifecycle) - “Prototype beans are cleaned up automatically” (
@PreDestroyNOT called for Prototype) - “All scopes work the same with lifecycle” (Prototype skips destruction stages)
- ”
@PostConstructand initialization are the same thing” (Instantiation != Initialization)
Related topics:
- [[07. What are the Bean Lifecycle stages]]
- [[08. What is BeanPostProcessor]]
- [[09. What do PostConstruct and PreDestroy methods do]]
- [[04. What is a Bean in Spring]]
- [[10. What is Bean scope]]