Inside Spring 6 Bean Creation: A Step‑by‑Step Deep Dive

This article walks through Spring 6.1.2’s bean creation lifecycle, from the refresh entry point to property population, initialization, and disposable bean registration, illustrating each step with concise code examples and detailed explanations.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Inside Spring 6 Bean Creation: A Step‑by‑Step Deep Dive

Environment: Spring 6.1.2

1. Introduction

To avoid interference from other classes (BeanPostProcessor), we use a clean GenericApplicationContext that does not register built‑in BeanPostProcessors.

public class Person {}
public static void main(String[] args) {
    try (GenericApplicationContext context = new GenericApplicationContext()) {
        context.registerBean(Person.class);
        context.refresh();
        System.out.println(context.getBean(Person.class));
    }
}

2. Core Entry Point

The container initialization starts at ConfigurableApplicationContext#refresh, which performs a series of steps.

public abstract class AbstractApplicationContext {
    public void refresh() throws BeansException, IllegalStateException {
        try {
            // 1. Prepare refresh (initialize Environment, validate properties)
            prepareRefresh();
            // 2. Obtain fresh BeanFactory
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            // 3. Prepare BeanFactory (register special post‑processors)
            prepareBeanFactory(beanFactory);
            // 4. Post‑process BeanFactory
            postProcessBeanFactory(beanFactory);
            // 5. Invoke BeanFactoryPostProcessors
            invokeBeanFactoryPostProcessors(beanFactory);
            // 6. Register BeanPostProcessors
            registerBeanPostProcessors(beanFactory);
            // 7. Initialize message source
            initMessageSource();
            // 8. Initialize event multicaster
            initApplicationEventMulticaster();
            // 9. Refresh (subclass‑specific logic)
            onRefresh();
            // 10. Register listeners
            registerListeners();
            // 11. Instantiate all non‑lazy singleton beans
            finishBeanFactoryInitialization(beanFactory);
            // 12. Finish refresh (publish ContextRefreshedEvent)
            finishRefresh();
        }
    }
}

This article focuses on step 11 – the bean creation process.

3. Bean Creation Process

3.1 Traversing BeanDefinitions

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    beanFactory.preInstantiateSingletons();
}

DefaultListableBeanFactory preInstantiateSingletons iterates over all bean definitions and obtains each bean.

public class DefaultListableBeanFactory {
    public void preInstantiateSingletons() {
        List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
        for (String beanName : beanNames) {
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                if (isFactoryBean(beanName)) {
                    Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                    if (bean instanceof SmartFactoryBean<?> smartFactoryBean && smartFactoryBean.isEagerInit()) {
                        getBean(beanName);
                    }
                } else {
                    getBean(beanName);
                }
            }
        }
    }
}

3.2 Getting Instance from Singleton Pool

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType,
                           @Nullable Object[] args, boolean typeCheckOnly) {
    String beanName = transformedBeanName(name);
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        // return existing instance
    } else {
        if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, () -> {
                try {
                    return createBean(beanName, mbd, args);
                } catch (Throwable ex) {
                    throw new BeansException("Failed to create bean", ex);
                }
            });
        } else if (mbd.isPrototype()) {
            // prototype handling
        } else {
            // other scopes
        }
    }
    return (T) sharedInstance;
}

3.3 Creating the Instance

3.3.1 Property Population

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
        if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
            return;
        }
    }
    // Autowiring (byName, byType) omitted for brevity
    for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
        PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
        if (pvsToUse == null) {
            return;
        }
        pvs = pvsToUse;
    }
}

Example: CommonAnnotationBeanPostProcessor processes @Resource fields.

public class CommonAnnotationBeanPostProcessor {
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
        try {
            metadata.inject(bean, beanName, pvs);
        } catch (Throwable ex) {
            // handle exception
        }
        return pvs;
    }
}

3.3.2 Initializing Bean

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    invokeAwareMethods(beanName, bean);
    Object wrappedBean = bean;
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    // invoke init methods (afterPropertiesSet, custom init-methods)
    invokeInitMethods(beanName, wrappedBean, mbd);
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    return wrappedBean;
}

3.3.3 Registering DisposableBean

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, getBeanPostProcessorCache().destructionAware));
        }
    }
}

The DisposableBeanAdapter checks for DisposableBean, destroy methods, AutoCloseable, etc., to ensure proper cleanup.

Thus the complete bean initialization flow in Spring 6 is demonstrated.

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.

javabackend-developmentspringdependency-injectionSpring Frameworkbean-lifecycle
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

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.