Backend Development 10 min read

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.

<code>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));
    }
}
</code>

2. Core Entry Point

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

<code>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();
        }
    }
}
</code>

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

3. Bean Creation Process

3.1 Traversing BeanDefinitions

<code>protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    beanFactory.preInstantiateSingletons();
}
</code>

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

<code>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);
                }
            }
        }
    }
}
</code>

3.2 Getting Instance from Singleton Pool

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

3.3 Creating the Instance

3.3.1 Property Population

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

Example: CommonAnnotationBeanPostProcessor processes @Resource fields.

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

3.3.2 Initializing Bean

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

3.3.3 Registering DisposableBean

<code>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));
        }
    }
}
</code>

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

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

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

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.