Foundation · Understand
The Bean Lifecycle
A bean is instantiated, populated, post-processed, initialized, used, and destroyed—in that order, with a hook at each seam.
Primary source: Spring Framework 6.2 Reference: Customizing the Nature of a Bean
Lesson 10 of 14
1d streakThe Architectural Question
Constructor bodies run before injection completes, so touching late-arriving dependencies there reads null and the failure appears only for specific wiring styles.
What this guide proves
- @PostConstruct
- @PreDestroy
- InitializingBean
The complete 10-step bean lifecycle timeline
From the moment Spring decides to instantiate a bean until its eventual disposal, AbstractAutowireCapableBeanFactory moves the object through a strict 10-step lifecycle:
| Step | Lifecycle Stage | Mechanism / Interface | Architectural Purpose |
|---|---|---|---|
| 1 | Instantiation | Constructor reflection or CGLIB | Allocates raw heap memory for the object instance. |
| 2 | Populate Properties | Dependency Injection | Resolves and injects constructor/setter collaborators. |
| 3 | Aware Callbacks | BeanNameAware, BeanFactoryAware | Supplies container infrastructure references if requested. |
| 4 | Pre-Initialization BPP | BeanPostProcessor.postProcessBeforeInitialization | Executes custom setup; CommonAnnotationBeanPostProcessor detects @PostConstruct. |
| 5 | @PostConstruct | @PostConstruct annotation | Primary initialisation hook for application code after dependencies are wired. |
| 6 | InitializingBean | InitializingBean.afterPropertiesSet() | Spring-specific programmatic initialization contract. |
| 7 | Custom init-method | @Bean(initMethod = "...") | Declarative configuration initialization hook. |
| 8 | Post-Initialization BPP | BeanPostProcessor.postProcessAfterInitialization | Generates dynamic AOP proxies (@Transactional, @Cacheable, @Async). |
| 9 | Ready for Use | Application traffic | Bean is cached in singleton registry and serves requests. |
| 10 | Destruction | @PreDestroy, DisposableBean.destroy() | Executes teardown logic and resource release on context shutdown. |
Verify the lifecycle with an executable probe
We can observe every lifecycle stage in sequence by creating a diagnostic probe component:
@Component
public class LifecycleAuditProbe implements BeanNameAware, InitializingBean, DisposableBean {
private static final Logger log = LoggerFactory.getLogger(LifecycleAuditProbe.class);
public LifecycleAuditProbe() {
log.info("1. Constructor executed (Instantiation)");
}
@Override
public void setBeanName(String name) {
log.info("2. BeanNameAware executed with name: {}", name);
}
@PostConstruct
public void postConstruct() {
log.info("3. @PostConstruct executed (Dependencies populated)");
}
@Override
public void afterPropertiesSet() {
log.info("4. InitializingBean.afterPropertiesSet() executed");
}
@PreDestroy
public void preDestroy() {
log.info("5. @PreDestroy executed (Container shutting down)");
}
@Override
public void destroy() {
log.info("6. DisposableBean.destroy() executed");
}
}Break it deliberately: Premature collaborator invocation in constructor
Invoking business operations or accessing proxy-wrapped features inside a class constructor fails because the bean lifecycle has only reached Step 1: dependencies may not be wired, and AOP proxies do not exist yet.
@Component
public class CacheWarmupService {
@Autowired
private RemoteDatabase database; // Field injection
public CacheWarmupService() {
// Step 1: Constructor runs BEFORE properties are populated!
database.loadAllData(); // Throws NullPointerException!
}
}The Golden Rule
Check your understanding
Active Recall Checkpoint
At what exact step in the bean lifecycle are dynamic AOP proxies (@Transactional, @Cacheable) created?
Hint: Before or after initialization?
Show answer & explanation
Dynamic AOP proxies are created in Step 8 during BeanPostProcessor.postProcessAfterInitialization(), after all properties are populated and @PostConstruct has finished.
Active Recall Checkpoint
What is the execution order between @PostConstruct, InitializingBean.afterPropertiesSet(), and a custom @Bean(initMethod)?
Hint: Annotation first, interface second, declarative last.
Show answer & explanation
The order is strictly: (1) @PostConstruct, (2) InitializingBean.afterPropertiesSet(), and (3) Custom initMethod.
Runtime Architecture & Flow
Spring container lifecycle
Construction is only the first lifecycle phase
Follow one singleton from its bean definition to the reference that application code actually receives, then through shutdown.
Phase 1 of 6
Spring reads the bean definition
Metadata tells the container which type to create, its scope, dependencies, and lifecycle callbacks.
Boundary rule
Use the constructor for required dependencies, initialization callbacks for validation or setup after wiring, and destruction callbacks only for resources the bean owns.
Interactive Spring Runtime Lab
Spring Bean Lifecycle & CGLIB Proxy Simulator
1. BeanDefinition Registration
ClassPathBeanDefinitionScanner registers AnnotatedGenericBeanDefinition with metadata (scope=singleton, lazyInit=false).
2026-10-19 12:00:00.124 [main] INFO org.springframework.boot.Startup:
> DEBUG o.s.b.f.s.DefaultListableBeanFactory: Creating shared instance of singleton bean 'orderService'
Working Implementation
Code Walkthrough
Use `@PostConstruct` to validate configuration or load initial cache data immediately after dependency injection completes.
package com.example.core;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
@Component
public class BeanLifecycleTimelineLab implements BeanNameAware, InitializingBean {
private String beanName;
// Step 1: JVM Constructor
public BeanLifecycleTimelineLab() {
System.out.println("[Lifecycle 1] Constructor: Memory allocated, properties not yet injected");
}
// Step 3: Aware callbacks
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("[Lifecycle 3] BeanNameAware: Injected bean name = " + beanName);
}
// Step 5: JSR-250 @PostConstruct
@PostConstruct
public void initPostConstruct() {
System.out.println("[Lifecycle 5] @PostConstruct: Dependencies populated, validating connections");
}
// Step 6: InitializingBean.afterPropertiesSet
@Override
public void afterPropertiesSet() {
System.out.println("[Lifecycle 6] InitializingBean: Framework initialization complete");
}
// Step 8: Destruction callback
@PreDestroy
public void cleanup() {
System.out.println("[Lifecycle 8] @PreDestroy: Closing connections and flushing buffers");
}
}Synthesis & Production Verification
Production Acceptance Criteria
- Sequence constructor, @PostConstruct, and afterPropertiesSet from log timestamps
- Load cache data in @PostConstruct without touching unwired fields
- Release resources in @PreDestroy and observe it during shutdown
Key Takeaway
The bean lifecycle progresses through 10 deterministic steps from raw heap instantiation to Aware callbacks, post-processing, initialization hooks, and AOP proxy generation.
Architectural & Technical Review
Core Interview Questions
Q01At what exact point in the 10-step bean lifecycle are dynamic AOP proxies (@Transactional, @Cacheable) generated?Show answer
Dynamic AOP proxies are created in Step 8 during BeanPostProcessor.postProcessAfterInitialization(), after all properties are populated and @PostConstruct has finished.
Q02What is the strict execution order between @PostConstruct, InitializingBean.afterPropertiesSet(), and custom initMethod?Show answer
The execution order is strictly: (1) @PostConstruct, (2) InitializingBean.afterPropertiesSet(), and (3) Custom initMethod.
Q03Why should heavy business logic or remote network calls never be placed in a class constructor?Show answer
Constructors run before dependencies are populated and before AOP proxies or security interceptors attach. Premature invocation causes NullPointerExceptions or transaction bypasses.
Production Pitfalls
Critical Anti-Patterns
- Pitfall: Executing database or network operations inside constructors — Constructors should only assign fields. Move startup initialization logic into @PostConstruct or ApplicationReadyEvent listeners.
- Pitfall: Relying on @Transactional behavior inside @PostConstruct — @PostConstruct runs before postProcessAfterInitialization, meaning dynamic AOP transaction proxies have not yet attached.
Primary Documentation & Specifications
Lesson Primary References
Supplementary Module Resources