Mastering Spring Bean Lifecycle: From Initialization to Destruction

This article walks through the complete Spring Bean lifecycle, explaining how the IoC container creates, initializes (including Aware callbacks, BeanPostProcessor hooks, and custom init methods) and finally destroys beans, with detailed code examples and source‑code analysis.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Mastering Spring Bean Lifecycle: From Initialization to Destruction

Introduction

This article explains how to manage the lifecycle of Spring beans within the Spring IoC container, covering bean creation, initialization (including Aware callbacks, BeanPostProcessor hooks, and custom init methods) and destruction.

Spring Bean Lifecycle

Below is the typical lifecycle flow of a Spring bean. The container first creates the bean instance, then populates its properties, and finally runs a series of callbacks before and after initialization.

Spring Bean lifecycle diagram
Spring Bean lifecycle diagram

Bean container finds the bean definition in the configuration file.

Bean container uses Java Reflection to create the bean instance.

If any properties are declared, they are set; if a property itself is a bean, it is resolved and set.

If the bean class implements BeanNameAware, the container calls setBeanName().

If the bean class implements BeanClassLoaderAware, the container calls setBeanClassLoader().

If the bean class implements BeanFactoryAware, the container calls setBeanFactory().

If a BeanPostProcessor is registered, its postProcessBeforeInitialization() method is invoked.

If the bean implements InitializingBean, the container calls afterPropertiesSet() after all properties are set.

If the bean definition contains an init-method, that method is invoked.

If a BeanPostProcessor is registered, its postProcessAfterInitialization() method is invoked.

If the bean implements DisposableBean, the container will call destroy() when the bean is no longer needed.

If the bean definition contains a destroy-method, that method is invoked.

Demo

The following demo shows the full lifecycle in action.

public class Person implements DisposableBean, InitializingBean, BeanFactoryAware, BeanNameAware {
    private String name;
    Person() {
        System.out.println("Constructor of person bean is invoked!");
    }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("setBeanFactory method of person is invoked");
    }
    @Override
    public void setBeanName(String name) {
        System.out.println("setBeanName method of person is invoked");
    }
    public void init() {
        System.out.println("custom init method of person bean is invoked!");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet method of person bean is invoked!");
    }
    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean Destroy method of person bean is invoked!");
    }
    public void destroyMethod() {
        System.out.println("custom Destroy method of person bean is invoked!");
    }
}
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("post Process Before Initialization is invoked");
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("post Process after Initialization is invoked");
        return bean;
    }
}
<?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 name="myBeanPostProcessor" class="ric.study.demo.ioc.life_cycle_demo_set.MyBeanPostProcessor"/>
    <bean name="personBean" class="ric.study.demo.ioc.life_cycle_demo_set.Person"
          init-method="init" destroy-method="destroyMethod">
        <property name="name" value="Richard Yi"/>
    </bean>
</beans>
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config-1.xml");
        ((ClassPathXmlApplicationContext) context).destroy();
    }
}

Running the demo produces the following output, which matches the lifecycle steps described above:

Constructor of person bean is invoked!
setBeanName method of person is invoked
setBeanFactory method of person is invoked
post Process Before Initialization is invoked
afterPropertiesSet method of person bean is invoked!
custom init method of person bean is invoked!
post Process after Initialization is invoked
DisposableBean Destroy method of person bean is invoked!
custom Destroy method of person bean is invoked!

Source Code Analysis

Below are excerpts from Spring's source code that implement the lifecycle callbacks.

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                invokeAwareMethods(beanName, bean);
                return null;
            }
        }, getAccessControlContext());
    } else {
        // invokeAwareMethods handles BeanNameAware, BeanClassLoaderAware, BeanFactoryAware
        invokeAwareMethods(beanName, bean);
    }
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        // BeanPostProcessor before-initialization callback
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    try {
        // init-method or afterPropertiesSet()
        invokeInitMethods(beanName, wrappedBean, mbd);
    } catch (Throwable ex) {
        throw new BeanCreationException((mbd != null ? mbd.getResourceDescription() : null), beanName,
                "Invocation of init method failed", ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
        // BeanPostProcessor after-initialization callback
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}
private void invokeAwareMethods(final String beanName, final Object bean) {
    if (bean instanceof Aware) {
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }
        if (bean instanceof BeanClassLoaderAware) {
            ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
        }
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
        }
    }
}
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        result = beanProcessor.postProcessBeforeInitialization(result, beanName);
        if (result == null) {
            return result;
        }
    }
    return result;
}
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable {
    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
        ((InitializingBean) bean).afterPropertiesSet();
    }
    if (mbd != null) {
        String initMethodName = mbd.getInitMethodName();
        if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName))
                && !mbd.isExternallyManagedInitMethod(initMethodName)) {
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}
protected void destroyBean(String beanName, DisposableBean bean) {
    if (bean != null) {
        try {
            bean.destroy();
        } catch (Throwable ex) {
            logger.error("Destroy method on bean with name '" + beanName + "' threw an exception", ex);
        }
    }
    if (this.destroyMethod != null) {
        // invoke custom destroy-method
        invokeCustomDestroyMethod(this.destroyMethod);
    }
}

BeanPostProcessor Registration

During container refresh, Spring registers all BeanPostProcessor beans. The process consists of four steps:

Obtain bean names that implement BeanPostProcessor via

beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false)

.

Classify them into PriorityOrdered, Ordered, and non‑ordered groups.

Register PriorityOrdered processors first (sorted), then Ordered (sorted), then the rest.

Finally, register internal post‑processors and an ApplicationListenerDetector at the end of the chain.

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
    int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
    beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
    // Separate by PriorityOrdered, Ordered, and non‑ordered
    List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    List<String> orderedPostProcessorNames = new ArrayList<>();
    List<String> nonOrderedPostProcessorNames = new ArrayList<>();
    for (String ppName : postProcessorNames) {
        if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            priorityOrderedPostProcessors.add(pp);
        } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
            orderedPostProcessorNames.add(ppName);
        } else {
            nonOrderedPostProcessorNames.add(ppName);
        }
    }
    // Register in order
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
    // Ordered
    List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
    for (String ppName : orderedPostProcessorNames) {
        BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
        orderedPostProcessors.add(pp);
    }
    sortPostProcessors(orderedPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, orderedPostProcessors);
    // Non‑ordered
    List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
    for (String ppName : nonOrderedPostProcessorNames) {
        BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
        nonOrderedPostProcessors.add(pp);
    }
    registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
    // Finally add ApplicationListenerDetector
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}

DisposableBeanAdapter Registration

When a bean is created, Spring registers a DisposableBeanAdapter if the bean requires destruction (singleton and implements DisposableBean, has a custom destroy method, or has DestructionAwareBeanPostProcessor s). This registration happens during the bean creation phase.

protected void doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException {
    // ... bean creation steps omitted ...
    // Register bean as disposable.
    try {
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    } catch (BeanDefinitionValidationException ex) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                "Invalid destruction signature", ex);
    }
    return exposedObject;
}
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
    if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
        if (mbd.isSingleton()) {
            registerDisposableBean(beanName,
                new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), null));
        } else {
            Scope scope = this.scopes.get(mbd.getScope());
            if (scope == null) {
                throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
            }
            scope.registerDestructionCallback(beanName,
                new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), null));
        }
    }
}

These mechanisms ensure that all necessary cleanup logic—custom destroy methods, DisposableBean callbacks, and post‑processor destruction hooks—is executed when the container shuts down.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaIoCbean-lifecycle
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.