Backend Development 15 min read

Understanding the Spring Bean Lifecycle: Creation, Initialization, Usage, and Destruction

This article explains the complete Spring bean lifecycle—including creation, property filling, initialization, usage, and destruction—detailing how Spring manages object creation, dependency injection, AOP proxy generation, and lifecycle callbacks through various internal phases and extension points.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Understanding the Spring Bean Lifecycle: Creation, Initialization, Usage, and Destruction

Among all aspects of Spring, the bean lifecycle is paramount because core capabilities such as object creation (IoC), property injection (DI), initialization method invocation, and AOP proxy generation are all performed during this lifecycle.

1. Bean Lifecycle

Spring's lifecycle can be roughly divided into the following stages: Creation → Property Filling → Initialization → Usage → Destruction . The article briefly describes each stage:

Creation stage : Spring creates the object, delegating object creation to the container (IoC).

Property filling stage : Spring injects dependencies by locating required beans and populating the corresponding fields.

Initialization bean stage : Complex operations such as invoking Aware interfaces, initialization methods, and generating AOP proxies occur here.

Usage bean stage : The fully initialized bean provides services during application runtime.

Destruction bean stage : The bean is destroyed when the container shuts down.

1.1 Creating a Bean

Object creation can be performed via new , reflection, clone , etc. Spring primarily uses reflection, but if a Supplier or factory method is supplied, Spring will use those instead.

Key source excerpt (simplified):

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    Class
beanClass = resolveBeanClass(mbd, beanName);
    Supplier
instanceSupplier = mbd.getInstanceSupplier();
    if (instanceSupplier != null) {
        return obtainFromSupplier(instanceSupplier, beanName);
    }
    if (mbd.getFactoryMethodName() != null) {
        return instantiateUsingFactoryMethod(beanName, mbd, args);
    }
    // ... constructor inference logic ...
    return instantiateBean(beanName, mbd);
}

The selection logic is:

If a Supplier is provided, use it.

If a factory method is defined, use it.

Otherwise, infer the constructor: prefer a no‑arg constructor, then a constructor annotated with @Autowired(required=true) , or the most suitable @Autowired constructor based on parameter count and type matching.

1.2 Merged BeanDefinition

Spring allows post‑processors of type MergedBeanDefinitionPostProcessor to modify the BeanDefinition . This extension point is used internally to process annotations such as @Autowired , @Resource , @PostConstruct , and @PreDestroy .

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class
beanType, String beanName) {
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof MergedBeanDefinitionPostProcessor) {
            ((MergedBeanDefinitionPostProcessor) bp).postProcessMergedBeanDefinition(mbd, beanType, beanName);
        }
    }
}

1.3 Exposing Factory Object

During this phase Spring places early bean references into the third‑level cache ( singletonFactories ) to support circular dependencies. If a circular reference is detected, the bean can be retrieved via getObject() from this cache.

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

1.4 Property Filling

This stage performs dependency injection, handling automatic injection, @Autowired , and @Resource . Spring determines whether to inject by name or by type and then invokes InstantiationAwareBeanPostProcessor#postProcessProperties() to complete the injection.

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    int resolvedAutowireMode = mbd.getResolvedAutowireMode();
    if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
        // name or type based injection logic
    }
    // invoke BeanPostProcessors for @Autowired/@Resource
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof InstantiationAwareBeanPostProcessor) {
            PropertyValues pvsToUse = ((InstantiationAwareBeanPostProcessor) bp)
                .postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
            // ... further processing ...
        }
    }
    // dependency check and apply property values
}

1.5 Initializing Bean

Initialization includes invoking Aware interfaces, @PostConstruct methods, custom init methods, and potentially creating AOP proxies. The process is:

Invoke Aware methods (e.g., BeanNameAware , BeanFactoryAware ).

Apply BeanPostProcessor s before initialization (e.g., ApplicationContextAwareProcessor , InitDestroyAnnotationBeanPostProcessor ).

Invoke init methods such as afterPropertiesSet() or custom initMethod .

Apply BeanPostProcessor s after initialization, which may generate AOP proxies for @Transactional , @Async , etc.

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    invokeAwareMethods(beanName, bean);
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    } catch (Throwable ex) {
        // handle init method errors
    }
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}

After initialization, the bean is stored in the singleton pool and becomes ready to serve requests.

2. Bean Lifecycle Overview

2.1 Bean Scanning Phase

During container startup, Spring scans classes (using ASM bytecode analysis) to find candidates annotated with @Component (or related stereotypes), creates BeanDefinition objects for them, and stores them in a collection for later instantiation.

2.2 Post‑Instantiation Callbacks

After all singleton beans are instantiated, Spring triggers callbacks such as @EventListener registration in the afterSingletonsInstantiated phase. Custom logic can be added by implementing SmartInitializingSingleton#afterSingletonsInstantiated() .

2.3 Bean Destruction Phase

When the container shuts down, beans that implement DisposableBean , AutoCloseable , or declare a destroy-method are invoked to release resources.

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
    // ... bean creation logic ...
    try {
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    } catch (BeanDefinitionValidationException ex) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }
    return exposedObject;
}

Overall, understanding each phase helps developers grasp how Spring transforms a plain Java object into a fully managed bean with powerful capabilities.

Thank you for reading; hope it helps :) Source: juejin.cn/post/7155884227714613285
backendJavaAOPIoCSpringDependency Injectionbean lifecycle
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login 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.