Unveiling Spring's Bean Lifecycle: From Retrieval to Destruction
This article provides a comprehensive, step‑by‑step walkthrough of Spring's Bean lifecycle—including bean retrieval, creation, property population, initialization, and destruction—illustrated with flowcharts and complete Java code snippets to help developers understand the inner workings of the framework.
1. Retrieving a Bean
The entry point for bean retrieval is the doGetBean method in AbstractBeanFactory. The process includes handling bean names that start with "&" (indicating a FactoryBean), checking the singleton cache, dealing with circular dependencies, and delegating to parent factories if necessary.
2. Creating a Bean
2.1 Before Instantiation
The method createBean in AbstractAutowireCapableBeanFactory performs several checks: it resolves the bean class, verifies visibility, and determines whether to apply InstantiationAwareBeanPostProcessor callbacks such as postProcessBeforeInstantiation and postProcessAfterInitialization. If these callbacks return a non‑null bean, the standard creation flow is short‑circuited.
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
// condition 1
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// condition 2 & 3
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
return bean;
}2.2 Actual Bean Creation (doCreateBean)
The doCreateBean method orchestrates the full creation process: it checks for an existing instance wrapper, creates the bean instance (via constructors or factory methods), resolves constructor arguments, applies post‑processors, and finally populates properties.
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Resolve bean class
Class<?> beanClass = resolveBeanClass(mbd, beanName);
if (beanClass == null) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non‑public access not allowed: " + beanClass.getName());
}
// Use supplier if available
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// Factory method handling
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Constructor resolution and autowiring
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// Default no‑arg constructor
return instantiateBean(beanName, mbd);
}2.3 Populating Bean Properties
The populateBean method applies property values, handling autowire‑by‑name and autowire‑by‑type, and invoking InstantiationAwareBeanPostProcessor callbacks for property processing.
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
if (bw == null) {
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
return;
}
// Apply InstantiationAwareBeanPostProcessors before property population
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
return;
}
}
}
// Resolve property values, autowire by name/type, and apply them
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
// ... (autowire and apply property values) ...
applyPropertyValues(beanName, mbd, bw, pvs);
}2.4 Initializing the Bean
During initialization, Spring invokes aware‑interface methods ( BeanNameAware, BeanFactoryAware, etc.), BeanPostProcessor callbacks ( postProcessBeforeInitialization, postProcessAfterInitialization), and the bean's own afterPropertiesSet method if it implements InitializingBean.
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
// Aware interfaces
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
// ... other aware interfaces ...
// BeanPostProcessor before initialization
Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
// Invoke custom init methods
if (bean instanceof InitializingBean) {
((InitializingBean) bean).afterPropertiesSet();
}
// Custom init-method
String initMethodName = (mbd != null ? mbd.getInitMethodName() : null);
if (StringUtils.hasLength(initMethodName) && !(bean instanceof InitializingBean && "afterPropertiesSet".equals(initMethodName))) {
invokeCustomInitMethod(beanName, bean, mbd);
}
// BeanPostProcessor after initialization
return applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}3. Destroy Method and Bean Destruction
When a bean is destroyed, Spring checks for a configured destroyMethodName or a DestructionAwareBeanPostProcessor. It registers a DisposableBeanAdapter that will invoke the appropriate destroy callbacks.
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
String destroyMethodName = inferDestroyMethodIfNecessary(bean, mbd);
if (destroyMethodName != null && !(bean instanceof DisposableBean && "destroy".equals(destroyMethodName))) {
if (!mbd.isExternallyManagedDestroyMethod(destroyMethodName)) {
DisposableBeanAdapter adapter = new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc);
registerDisposableBean(beanName, adapter);
}
}
}The DisposableBeanAdapter implements Runnable and, when executed, calls the bean's destroy method (either via DisposableBean or a custom destroy method) while handling security contexts and logging.
public class DisposableBeanAdapter implements Runnable {
private final Object bean;
private final String beanName;
private final boolean invokeDisposableBean;
private final AccessControlContext acc;
// constructor omitted for brevity
@Override
public void run() {
if (invokeDisposableBean) {
((DisposableBean) bean).destroy();
} else {
// invoke custom destroy method via reflection
}
}
}4. Summary
The article walks through the entire Spring Bean lifecycle with detailed flowcharts: preparation before instantiation, the actual creation steps, property population, initialization callbacks, and finally destruction handling. Each stage is backed by concrete code excerpts from the Spring framework, providing a clear map for developers to follow.
Images
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
