Every Spring Bean goes through a defined lifecycle managed by the ApplicationContext. Understanding this lifecycle — especially the callback order — is one of the most frequently tested topics on the VMware Spring Professional exam (2V0-72.22).
Lifecycle Overview
The full lifecycle has 7 phases:
- Instantiation — the bean object is created
- Dependency Injection — properties and dependencies are set
- Aware Interfaces — Spring injects infrastructure references
- BeanPostProcessor — before initialization — cross-cutting logic runs before init callbacks
- Initialization Callbacks — custom init logic runs (
@PostConstruct,afterPropertiesSet, init-method) - BeanPostProcessor — after initialization — cross-cutting logic runs after init callbacks
- Bean is Ready — the bean serves requests until context shutdown triggers Destruction Callbacks
Phase 1: Instantiation
Spring creates the bean instance using one of:
- Constructor with parameters — the most common approach in modern Spring (constructor injection)
- A
@Beanfactory method in a@Configurationclass - A no-arg constructor — when no dependencies need injection
@Component
public class OrderService {
private final PaymentService paymentService;
// Spring calls this constructor and injects PaymentService
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}For @Bean methods, Spring calls the method itself as a factory:
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserService(); // Spring calls this factory method
}
}Phase 2: Dependency Injection
After the bean is instantiated, Spring populates any remaining dependencies via:
- Constructor injection (preferred — dependencies are set during instantiation)
- Setter injection — via
@Autowiredon setter methods - Field injection — via
@Autowiredon fields (not recommended, makes testing harder)
@Component
public class NotificationService {
private EmailSender emailSender;
// Setter injection
@Autowired
public void setEmailSender(EmailSender emailSender) {
this.emailSender = emailSender;
}
}Note: With constructor injection, phases 1 and 2 effectively happen together — dependencies are injected through the constructor at instantiation time. Setter and field injection happen as a separate step after the object is created.
Phase 3: Aware Interfaces
After dependency injection, Spring checks if the bean implements any Aware interfaces and injects the corresponding infrastructure objects. The following three are called directly by the container (invokeAwareMethods) in this order:
BeanNameAware.setBeanName(String name)— receives the bean's name in the containerBeanClassLoaderAware.setBeanClassLoader(ClassLoader cl)— receives the classloaderBeanFactoryAware.setBeanFactory(BeanFactory factory)— receives the owningBeanFactory
@Component
public class AuditService implements BeanNameAware, BeanFactoryAware {
private String beanName;
@Override
public void setBeanName(String name) {
this.beanName = name; // e.g. "auditService"
}
@Override
public void setBeanFactory(BeanFactory factory) {
// Access to the owning BeanFactory
}
}Additional Aware interfaces — ApplicationContextAware, EnvironmentAware, ResourceLoaderAware, ApplicationEventPublisherAware, and MessageSourceAware — are handled by ApplicationContextAwareProcessor, which is a BeanPostProcessor. They are called during Phase 4 (BPP before initialization), not in this phase. The practical effect is the same — they still run before @PostConstruct — but the mechanism is different.
Exam tip: In modern Spring, prefer
@Autowiredor constructor injection to get theApplicationContextinstead of implementingApplicationContextAware. The Aware interfaces exist for framework-level code or cases where injection is not possible.
Phase 4: BeanPostProcessor — Before Initialization
BeanPostProcessor (BPP) is a powerful extension point that lets you modify or wrap beans before and after their initialization callbacks. Spring calls postProcessBeforeInitialization() on every bean for each registered BPP.
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("Before init: " + beanName);
return bean; // return the (possibly modified) bean
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("After init: " + beanName);
return bean;
}
}@PostConstruct is processed by a BeanPostProcessor
This is a critical detail for the exam: @PostConstruct is not built into Spring's core initialization — it is handled by CommonAnnotationBeanPostProcessor, which is itself a BeanPostProcessor. During its postProcessBeforeInitialization() call, it detects and invokes @PostConstruct methods.
This means @PostConstruct runs during the BPP "before initialization" phase, before afterPropertiesSet() and custom init-methods.
Phase 5: Initialization Callbacks
After BPP pre-processing (including @PostConstruct), Spring calls the remaining initialization callbacks in this order:
1. InitializingBean.afterPropertiesSet()
@Component
public class CacheService implements InitializingBean {
@Override
public void afterPropertiesSet() {
// Called by Spring after @PostConstruct
}
}2. Custom init-method
@Bean(initMethod = "setup")
public CacheService cacheService() {
return new CacheService();
}Exam tip: The full initialization order is:
@PostConstruct(via BPP) →afterPropertiesSet()→ custominit-method. All three are optional — you can use any combination.
Phase 6: BeanPostProcessor — After Initialization
After all init callbacks complete, Spring calls postProcessAfterInitialization() on each registered BPP. This is where Spring creates AOP proxies — wrapping the bean in a proxy that applies aspect advice.
Exam tip: This is why the
@PostConstructmethod itself cannot be intercepted by AOP advice — the proxy does not exist yet during initialization. The proxy is created in this phase, after init callbacks have already run.
Phase 7: Bean Is Ready → Destruction
The bean is now fully initialized and available in the ApplicationContext. For singleton-scoped beans, it remains in the container until shutdown.
When the ApplicationContext is closed — via context.close() or a JVM shutdown hook (context.registerShutdownHook()) — Spring calls destruction callbacks in this order:
1. @PreDestroy (recommended)
@Component
public class CacheService {
@PreDestroy
public void cleanup() {
System.out.println("Clearing cache...");
}
}2. DisposableBean.destroy()
@Component
public class CacheService implements DisposableBean {
@Override
public void destroy() {
// Called by Spring on context shutdown
}
}3. Custom destroy-method
@Bean(destroyMethod = "shutdown")
public CacheService cacheService() {
return new CacheService();
}Important: Destruction callbacks are not called for prototype-scoped beans. Spring does not track prototype instances after creation. See our Bean Scopes guide for details.
Complete Callback Order
This is the full sequence that Spring follows for each singleton bean — memorize it for the exam:
| Step | Callback | Mechanism |
|---|---|---|
| 1 | Instantiation | Constructor or factory method |
| 2 | Dependency injection | @Autowired, setter, or field injection |
| 3 | setBeanName() | BeanNameAware |
| 4 | setBeanClassLoader() | BeanClassLoaderAware |
| 5 | setBeanFactory() | BeanFactoryAware |
| 6 | postProcessBeforeInitialization() | BeanPostProcessor (includes @PostConstruct via CommonAnnotationBeanPostProcessor and setApplicationContext() via ApplicationContextAwareProcessor) |
| 7 | afterPropertiesSet() | InitializingBean |
| 8 | Custom init-method | @Bean(initMethod = "...") |
| 9 | postProcessAfterInitialization() | BeanPostProcessor (AOP proxies created here) |
| 10 | Bean is ready | — |
| 11 | @PreDestroy | CommonAnnotationBeanPostProcessor (via DestructionAwareBeanPostProcessor) |
| 12 | destroy() | DisposableBean |
| 13 | Custom destroy-method | @Bean(destroyMethod = "...") |
Exam Quick Reference
Q: Which initialization callback runs first — @PostConstruct or afterPropertiesSet()?
@PostConstruct runs first. It is invoked by CommonAnnotationBeanPostProcessor during the BPP postProcessBeforeInitialization phase, before afterPropertiesSet().
Q: Do destruction callbacks run for prototype-scoped beans? No. Spring does not manage the full lifecycle of prototype beans. After returning a prototype instance, Spring does not call destroy callbacks on it.
Q: What triggers the destruction phase?
Calling context.close() or registering a JVM shutdown hook via context.registerShutdownHook().
Q: Where in the lifecycle are AOP proxies created?
During BeanPostProcessor.postProcessAfterInitialization() — after all init callbacks have completed. This is why @PostConstruct methods cannot be intercepted by AOP advice.
Q: What is the role of BeanPostProcessor?
BPP is a container extension point that can modify or wrap any bean before and after initialization. Spring uses BPPs internally — for example, CommonAnnotationBeanPostProcessor processes @PostConstruct and @PreDestroy, and AutowiredAnnotationBeanPostProcessor handles @Autowired.
Frequently Asked Questions
What is the Spring Bean lifecycle?
The Spring Bean lifecycle is the sequence of steps Spring follows when creating, configuring, and destroying a bean. It includes instantiation, dependency injection, Aware interface callbacks, BeanPostProcessor hooks, initialization callbacks (@PostConstruct, afterPropertiesSet, init-method), and destruction callbacks (@PreDestroy, destroy, destroy-method). Understanding this order is essential for the VMware Spring Professional exam.
What is the difference between @PostConstruct and afterPropertiesSet()?
Both are initialization callbacks, but they differ in mechanism and priority. @PostConstruct is a standard Java annotation (JSR-250) processed by CommonAnnotationBeanPostProcessor — it runs first. afterPropertiesSet() requires implementing the InitializingBean interface and runs second. @PostConstruct is preferred because it does not couple your code to the Spring API.
Why does @PreDestroy not work on prototype beans?
Spring does not track prototype bean instances after creation. Once a prototype bean is returned to the caller, Spring no longer holds a reference to it. Without a reference, Spring cannot invoke destruction callbacks when the context shuts down. You must manage cleanup of prototype beans manually.
Can @PostConstruct be intercepted by Spring AOP?
No. AOP proxies are created during BeanPostProcessor.postProcessAfterInitialization(), which runs after @PostConstruct. At the time @PostConstruct executes, the bean is not yet wrapped in a proxy, so AOP advice does not apply to initialization methods.
→ Test your lifecycle knowledge with practice questions — 10 exam-style questions covering bean lifecycle, scopes, and more
→ Spring Bean Scopes explained — how scope affects lifecycle and destruction behavior
→ Spring AOP & Pointcut guide — understand where AOP fits in the lifecycle
→ Get Full Access — 970+ Questions — full practice exam bank with detailed explanations