Understanding the Instantiation and Initialization Phases of Spring Bean Lifecycle
Spring Bean Lifecycle Overview
A Bean in the Spring framwork undergoes a multi-stage lifecycle from creation to destruction. The following diagram illustrates the core stages of a Bean's lifecycle:
During these lifecycle phases, Spring provides corresponding interfaces and annotations, allowing developers to perform custom operations at specific points.
Practical Implementation Examples
Creating a Spring Context Utility
A common application of lifecycle interfaces is building a utility class to access the Spring ApplicationContext. This class is annotated with @Component to register it as a Bean, and it implements the ApplicationContextAware interface. When this Bean is created and initialized, Spring will invoke the setApplicationContext method, providing the context.
@Component
public class ContextHolder implements ApplicationContextAware {
private static ApplicationContext ctx;
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
ContextHolder.ctx = ctx;
}
public static ApplicationContext getContext() {
if (ctx == null) {
throw new IllegalStateException("ApplicationContext is not available.");
}
return ctx;
}
public static Object getBean(String beanName) {
return getContext().getBean(beanName);
}
public static <T> T getBean(String beanName, Class<T> beanClass) {
return getContext().getBean(beanName, beanClass);
}
public static <T> T getBean(Class<T> beanClass) {
return getContext().getBean(beanClass);
}
}
Executing Post-Dependency Injection Initialization
When a Bean requires initialization after its dependencies have been injected, the @PostConstruct annotation can be used. In this example, DataService needs to call a method on its injected DataLoader dependency to populate initial data.
@Service
public class DataService {
private DataLoader loader;
private List<String> cachedData;
@Autowired
public void setDataLoader(DataLoader loader) {
this.loader = loader;
}
@PostConstruct
public void loadCache() {
this.cachedData = loader.fetchInitialData();
}
public void process() {
// Business logic
}
}
@Service
class DataLoader {
public List<String> fetchInitialData() {
// Implementation
return new ArrayList<>();
}
}
@PostConstruct is a standard JSR-250 annotation. Alternative initialization mechanisms include implementing the InitializingBean interface's afterPropertiesSet method, or specifying an initialization method via XML init-method configuraton or the @Bean annotation's initMethod attribute.
Core Bean Creation Process in Spring Source Code
Spring's internal bean creation process consists of three primary phases: Instantiation -> Property Population -> Initialization. The property population phase handles dependency injection (e.g., @Autowired). The initialization phase encompasses all steps from calling XXXAware.setXXX() methods to postProcessAfterInitialization().
The doCreateBean method in AbstractAutowireCapableBeanFactory demonstrates this structure:
protected Object doCreateBean(String beanName, RootBeanDefinition def, Object... args) {
BeanWrapper wrapper = null;
if (wrapper == null) {
// 1. Instantiate
wrapper = createBeanInstance(beanName, def, args);
}
Object beanInstance = wrapper.getWrappedInstance();
// Early singleton exposure logic...
Object exposedObject = beanInstance;
try {
// 2. Populate properties
populateBean(beanName, def, wrapper);
// 3. Initialize
exposedObject = initializeBean(beanName, exposedObject, def);
} catch (Throwable ex) {
// Handle exception
}
return exposedObject;
}
The initializeBean() Method Breakdown
Within initializeBean(), execution proceeds in a specific order: invokeAwareMethods() -> applyBeanPostProcessorsBeforeInitialization() -> invokeInitMethods() -> applyBeanPostProcessorsAfterInitialization().
protected Object initializeBean(String name, Object bean, RootBeanDefinition def) {
// Step 1: Call Aware methods
invokeAwareMethods(name, bean);
Object processedBean = bean;
if (def == null || !def.isSynthetic()) {
// Step 2: BeanPostProcessor.pre-init
processedBean = applyBeanPostProcessorsBeforeInitialization(processedBean, name);
}
try {
// Step 3: Call init methods
invokeInitMethods(name, processedBean, def);
} catch (Throwable ex) {
throw new BeanCreationException(name, "Init method failed", ex);
}
if (def == null || !def.isSynthetic()) {
// Step 4: BeanPostProcessor.post-init
processedBean = applyBeanPostProcessorsAfterInitialization(processedBean, name);
}
return processedBean;
}
The invokeAwareMethods() Method
The invokeAwareMethods() method only handles the BeanNameAware, BeanClassLoaderAware, and BeanFactoryAware interfaces. Other Aware interfaces, such as ApplicationContextAware, are processed by specific BeanPostProcessor implementations like ApplicationContextAwareProcessor within its postProcessBeforeInitialization() method.
The applyBeanPostProcessorsBeforeInitialization() Method
This method iterates through all registered BeanPostProcessor beans and calls their postProcessBeforeInitialization() method.
public Object applyBeanPostProcessorsBeforeInitialization(Object bean, String name) throws BeansException {
Object result = bean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, name);
if (current == null) {
return result;
}
result = current;
}
return result;
}
Spring's CommonAnnotationBeanPostProcessor (which also handles @Resource) is a BeanPostProcessor that processes the @PostConstruct annotation. Its constructor registers the annotation type, and its parent class InitDestroyAnnotationBeanPostProcessor implements the logic to find and invoke methods annotated with @PostConstruct in its postProcessBeforeInitialization().
The invokeInitMethods() Method
This method first checks if the bean implements InitializingBean and calls afterPropertiesSet(). It then checks the bean definition for any custom initialization method names and invokes them via reflection.
protected void invokeInitMethods(String name, Object bean, RootBeanDefinition def) throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (def == null || !def.isExternallyManagedInitMethod("afterPropertiesSet"))) {
// Call afterPropertiesSet
((InitializingBean) bean).afterPropertiesSet();
}
if (def != null && bean.getClass() != NullBean.class) {
String[] customInitMethods = def.getInitMethodNames();
if (customInitMethods != null) {
for (String methodName : customInitMethods) {
if (isValidCustomMethod(isInitializingBean, methodName, def)) {
// Invoke custom init method via reflection
invokeCustomInitMethod(name, bean, def, methodName);
}
}
}
}
}
The applyBeanPostProcessorsAfterInitialization() Method
Similar to its "Before" counterpart, this method iterates through BeanPostProcessor instances and calls their postProcessAfterInitialization() method, allowing for further modification of the bean after initialization.