Backend Development 16 min read

Understanding the Spring Bean Lifecycle and Its Core Stages

This article explains the complete lifecycle of a Spring bean—including creation, merged BeanDefinition processing, factory exposure, property population, initialization, and destruction—while illustrating the underlying source‑code mechanisms and how Spring resolves constructors, handles circular dependencies, and integrates AOP and post‑processors.

Top Architect
Top Architect
Top Architect
Understanding the Spring Bean Lifecycle and Its Core Stages

In Spring, the bean lifecycle is central to the framework’s core capabilities such as IoC, DI, initialization callbacks, and AOP proxy generation. Understanding each phase clarifies how ordinary Java objects become powerful Spring beans.

1. Bean lifecycle stages – The lifecycle proceeds through creation → property filling → initialization → usage → destruction . Each stage performs specific tasks that enable Spring’s features.

2. Creating a bean – Spring usually creates objects via reflection, but it can also use a supplied Supplier or a factory method. The source code in AbstractAutowireCapableBeanFactory#createBeanInstance shows the decision flow:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    // resolve class
    Class
beanClass = resolveBeanClass(mbd, beanName);
    // 1. Supplier
    Supplier
instanceSupplier = mbd.getInstanceSupplier();
    if (instanceSupplier != null) {
        return obtainFromSupplier(instanceSupplier, beanName);
    }
    // 2. Factory method
    if (mbd.getFactoryMethodName() != null) {
        return instantiateUsingFactoryMethod(beanName, mbd, args);
    }
    // 3. Constructor inference
    Constructor
[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
        return autowireConstructor(beanName, mbd, ctors, args);
    }
    // 4. Default no‑arg constructor
    return instantiateBean(beanName, mbd);
}

The algorithm first checks for a Supplier , then a factory method, and finally infers the appropriate constructor, preferring one annotated with @Autowired(required=true) or the most suitable candidate based on parameter count and type matching.

3. Merged BeanDefinition – After creation, Spring applies MergedBeanDefinitionPostProcessor implementations (e.g., ApplicationContextAwareProcessor ) to enrich the BeanDefinition with metadata such as @Autowired , @Resource , @PostConstruct , and @PreDestroy handling.

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

4. Exposing factory objects – To support circular dependencies, Spring caches early bean references in the third‑level cache ( singletonFactories ) using addSingletonFactory so that dependent beans can obtain a reference via getObject() before full initialization.

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

5. Property population – Spring injects dependencies by name or type, processes Autowired and Resource annotations via InstantiationAwareBeanPostProcessor#postProcessProperties , and finally applies the resolved PropertyValues to the bean.

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

6. Bean initialization – Spring invokes aware‑interface callbacks, @PostConstruct methods, custom init methods, and then applies post‑processors that may generate AOP proxies (e.g., InfrastructureAdvisorAutoProxyCreator for @Transactional ).

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    invokeAwareMethods(beanName, bean);
    Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    } finally {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}

7. Bean destruction – When the container shuts down, beans implementing DisposableBean , AutoCloseable , or declaring a destroy-method are registered for disposal, ensuring proper resource cleanup.

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;
}

8. Scanning and registration – During container startup, Spring scans classpath using ASM, identifies components annotated with @Component , @Configuration , etc., creates BeanDefinition objects, and stores them for later instantiation.

Overall, the article provides a detailed walkthrough of how Spring manages bean lifecycles, from classpath scanning to creation, dependency injection, initialization, proxy generation, usage, and eventual destruction, illustrating the framework’s internal mechanisms with concrete source‑code excerpts.

BackendJavaSpringdependency injectionSpring Frameworkbean lifecycle
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.