Understanding the Lifecycle Phases of a Spring Bean
A Spring bean managed by an IoC container passes through distinct lifecycle stages. When configured via XML with initialization and destruction callbacks, the sequence is as follows:
- Bean Enstantiation: The container creates an instance by calling the constructor of the bean class.
- Property Injection: Dependencies are injected, typically through setter methods.
- Initialization Callbakc: If the
init-methodattribute is specified, the designated method is invoked after properties are set. - Ready for Use: The bean is fully initialized and can be retrieved via
getBean(). - Destruction Callback: When the container is shut down (e.g.,
close()is called on aClassPathXmlApplicationContext), thedestroy-methodis executed.
The following example demonstrates this flow.
Employee class with lifecycle callbacks:
package com.example.model;
public class Employee {
private int id;
private String fullName;
private String email;
public Employee() {
System.out.println("Step 1: Employee instance constructed");
}
public void setId(int id) {
this.id = id;
}
public void setFullName(String fullName) {
System.out.println("Step 2: Injecting fullName property");
this.fullName = fullName;
}
public void setEmail(String email) {
this.email = email;
}
public void startup() {
System.out.println("Step 3: Initialization callback - startup()");
}
public void cleanup() {
System.out.println("Step 5: Destruction callback - cleanup()");
}
@Override
public String toString() {
return "Employee{id=" + id + ", fullName='" + fullName + "', email='" + email + "'}";
}
}
XML configuration (app-config.xml):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="emp" class="com.example.model.Employee"
init-method="startup" destroy-method="cleanup">
<property name="fullName" value="Alice"/>
<property name="id" value="101"/>
</bean>
</beans>
Test driver:
package com.example.test;
import com.example.model.Employee;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class LifecycleDemo {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext("app-config.xml");
Employee emp = ctx.getBean("emp", Employee.class);
System.out.println("Step 4: Retrieved bean - " + emp);
ctx.close();
}
}
Enhancing the Lifecycle with a BeanPostProcessor
To inject custom logic around initialization, implement the BeanPostProcessor interface. The container then adds two additional callback points:
postProcessBeforeInitialization(Object bean, String beanName)– called before theinit-method(orafterPropertiesSet).postProcessAfterInitialization(Object bean, String beanName)– called after the initialization.
With a registered BeanPostProcessor, the full lifecycle sequence expands to:
- Constructor execution
- Setter-based property injection
postProcessBeforeInitializationinit-methodinvocationpostProcessAfterInitialization- Bean ready for use (retrieved via
getBean()) - Destruction callback (
destroy-method)
Example processor:
package com.example.processor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MonitoringProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Before init for bean: " + beanName);
return bean; // must return the bean instance
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("After init for bean: " + beanName);
return bean;
}
}
Register it in the same XML file as a normal bean:
<bean id="monitor" class="com.example.processor.MonitoringProcessor"/>
Key points about BeanPostProcessor:
- Both interface methods must return the original bean instance (or a proxy if needed); returning
nullwill lead togetBean()failing to resolve the target. - An
ApplicationContextautomatically detectsBeanPostProcessorimplementations defined in the configuration and applies them to every bean. When working directly with aBeanFactory, explicit registration viaConfigurableBeanFactory.addBeanPostProcessor(..)is required.