Unlocking Spring’s Core: A Deep Dive into the IOC Container Mechanics

This article explores the fundamental mechanisms of the Spring container, detailing its architecture, core jars, bean factory processes, configuration parsing, bean instantiation, property population, and design patterns such as Factory, Singleton, Builder, Decorator, Observer, and Strategy, while providing extensive code examples and diagrams.

Alibaba Cloud Developer
Alibaba Cloud Developer
Alibaba Cloud Developer
Unlocking Spring’s Core: A Deep Dive into the IOC Container Mechanics

Problem

What is the Spring container?

How does the Spring container start?

What is the essence of the Spring container?

What role does the Spring container play in the Spring family?

What is the design philosophy of the Spring container?

Keywords

container, session, context, factory, registry, resolution, definition, initialization, lazy loading, BeanFactory, BeanDefinition, ApplicationContext

Full Overview

The Spring container essentially stores definition units that describe the properties and methods of various objects; when needed, it creates objects via reflection and initializes described properties. This involves several elegant design patterns and implementation ideas, providing an excellent template for writing standard high‑quality code. This article aims to discuss the most core mechanisms of the Spring container using the minimal amount of code.

Architecture

The overall Spring framework architecture is shown below. The foundation of Spring is the core container, i.e., the IOC container. On this basis, AOP, DATA, and WEB flourish. This chapter focuses on the core of the Spring family—the IOC container—while temporarily omitting other component functionalities.

The three core JARs of the Spring container are bean , context , and core . bean is the cornerstone, everything is a bean; context maintains the application context, acting as the stage; core provides the props.

ApplicationContext

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {<br/>    String getId();<br/>    String getApplicationName();<br/>    String getDisplayName();<br/>    long getStartupDate();<br/>    ApplicationContext getParent();<br/>    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;<br/>}

ApplicationContext: the basic class extends resources, messages, events, environment, and factory capabilities; it only contains simple read‑only properties.

ConfigurableApplicationContext: adds lifecycle control and extends ApplicationContext with environment, events, etc.

AbstractApplicationContext: most capabilities are defined here; it extends DefaultResourceLoader and ConfigurableApplicationContext, parsing the IOC container startup process in detail.

GenericApplicationContext, AnnotationConfigApplicationContext, GenericGroovyApplicationContext, GenericXmlApplicationContext, StaticApplicationContext, AbstractRefreshableApplicationContext, AbstractRefreshableConfigApplicationContext, AbstractXmlApplicationContext, ClassPathXmlApplicationContext, FileSystemXmlApplicationContext: various concrete context implementations.

BeanFactory

AliasRegistry: alias registration

BeanFactory: factory

BeanDefinitionRegistry: definition

DefaultListableBeanFactory consolidates the above capabilities, providing the core BeanDefinitionRegistry and BeanFactory for bean definition and creation.

Process

The container startup core process is illustrated below: the container reads configuration files, creates objects, then initializes object properties.

Start Container

To explore the most core mechanism of Spring, we first eliminate interference from higher‑level features, using the minimal JARs to build the project, then trace the container startup step by step.

Create a Java project and add the minimal required Spring dependencies.

package com.alibaba.spring;<br/><br/>import com.alibaba.spring.beans.Producer;<br/>import org.springframework.context.support.ClassPathXmlApplicationContext;<br/><br/>/**<br/> * @author Lin ZhenHua<br/> */<br/>public class Application {<br/>    public static void main(String[] args) {<br/>        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");<br/>        Producer producer = context.getBean(Producer.class);<br/>        System.out.println("running the test case with name = " + producer.getCount());<br/>    }<br/>}

Create the Spring XML configuration file:

<?xml version="1.0" encoding="UTF-8"?><br/><beans xmlns="http://www.springframework.org/schema/beans"<br/>      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br/>      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"><br/>    <bean name="producer" class="com.alibaba.spring.beans.Producer"><br/>        <property name="count" value="10"/><br/>    </bean><br/></beans>

Running the program yields:

July 10, 2018 11:31:16 INFO org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh<br/>Info: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@5197848c: startup date [Tue Jul 10 11:31:16 CST 2018]; root of context hierarchy<br/>July 10, 2018 11:31:17 INFO org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions<br/>Info: Loading XML bean definitions from class path resource [applicationContext.xml]<br/>running the test case with name = 10

Thus we have successfully created the object described in the configuration file via Spring’s IOC container without using the new operator. Next we will deeply analyze the IOC container workflow.

Entry

public Object getBean(String name) throws BeansException {<br/>    return doGetBean(name, null, null, false);<br/>}

The refresh() method ultimately calls finishBeanFactoryInitialization(beanFactory), which performs bean instantiation. The following sections dissect this method.

finishBeanFactoryInitialization

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {<br/>    // Initialize conversion service if present<br/>    if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {<br/>        beanFactory.setConversionService(beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));<br/>    }<br/>    // Register default embedded value resolver if none<br/>    if (!beanFactory.hasEmbeddedValueResolver()) {<br/>        beanFactory.addEmbeddedValueResolver(str -> getEnvironment().resolvePlaceholders(str));<br/>    }<br/>    // Initialize LoadTimeWeaverAware beans early<br/>    String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);<br/>    for (String weaverAwareName : weaverAwareNames) {<br/>            getBean(weaverAwareName);<br/>    }<br/>    beanFactory.setTempClassLoader(null);<br/>    beanFactory.freezeConfiguration();<br/>    beanFactory.preInstantiateSingletons();<br/>}

preInstantiateSingletons

public void preInstantiateSingletons() throws BeansException {<br/>    if (logger.isDebugEnabled()) {<br/>        logger.debug("Pre-instantiating singletons in " + this);<br/>    }<br/>    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);<br/>    for (String beanName : beanNames) {<br/>        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);<br/>        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {<br/>            if (isFactoryBean(beanName)) {<br/>                FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);<br/>                boolean isEagerInit;<br/>                if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {<br/>                    isEagerInit = AccessController.doPrivileged(() -> ((SmartFactoryBean<?>) factory).isEagerInit(), getAccessControlContext());<br/>                } else {<br/>                    isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit());<br/>                }<br/>                if (isEagerInit) {<br/>                    getBean(beanName);<br/>                }<br/>            } else {<br/>                getBean(beanName);<br/>            }<br/>        }<br/>    }<br/>    // post‑initialization callbacks<br/>    for (String beanName : beanNames) {<br/>        Object singletonInstance = getSingleton(beanName);<br/>        if (singletonInstance instanceof SmartInitializingSingleton) {<br/>            SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;<br/>            if (System.getSecurityManager() != null) {<br/>                AccessController.doPrivileged(() -> {<br/>                    smartSingleton.afterSingletonsInstantiated();<br/>                    return null;<br/>                }, getAccessControlContext());<br/>            } else {<br/>                smartSingleton.afterSingletonsInstantiated();<br/>            }<br/>        }<br/>    }<br/>}

Bean Creation Core

protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {<br/>    if (logger.isDebugEnabled()) {<br/>        logger.debug("Creating instance of bean '" + beanName + "'");<br/>    }<br/>    Class<?> resolvedClass = resolveBeanClass(mbd, beanName);<br/>    if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {<br/>        mbd = new RootBeanDefinition(mbd);<br/>        mbd.setBeanClass(resolvedClass);<br/>    }<br/>    mbd.prepareMethodOverrides();<br/>    try {<br/>        Object bean = resolveBeforeInstantiation(beanName, mbd);<br/>        if (bean != null) {<br/>            return bean;<br/>        }<br/>    } catch (Throwable ex) {<br/>        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex);<br/>    }<br/>    Object beanInstance = doCreateBean(beanName, mbd, args);<br/>    if (logger.isDebugEnabled()) {<br/>        logger.debug("Finished creating instance of bean '" + beanName + "'");<br/>    }<br/>    return beanInstance;<br/>}

doCreateBean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException {<br/>    BeanWrapper instanceWrapper = null;<br/>    if (mbd.isSingleton()) {<br/>        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);<br/>    }<br/>    if (instanceWrapper == null) {<br/>        instanceWrapper = createBeanInstance(beanName, mbd, args);<br/>    }<br/>    final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);<br/>    Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);<br/>    mbd.resolvedTargetType = beanType;<br/>    synchronized (mbd.postProcessingLock) {<br/>        if (!mbd.postProcessed) {<br/>            try {<br/>                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);<br/>            } catch (Throwable ex) {<br/>                throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex);<br/>            }<br/>            mbd.postProcessed = true;<br/>        }<br/>    }<br/>    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));<br/>    if (earlySingletonExposure) {<br/>        if (logger.isDebugEnabled()) {<br/>            logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");<br/>        }<br/>        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));<br/>    }<br/>    Object exposedObject = bean;<br/>    try {<br/>        populateBean(beanName, mbd, instanceWrapper);<br/>        if (exposedObject != null) {<br/>            exposedObject = initializeBean(beanName, exposedObject, mbd);<br/>        }<br/>    } catch (Throwable ex) {<br/>        if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {<br/>            throw (BeanCreationException) ex;<br/>        } else {<br/>            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);<br/>        }<br/>    }<br/>    if (earlySingletonExposure) {<br/>        Object earlySingletonReference = getSingleton(beanName, false);<br/>        if (earlySingletonReference != null && exposedObject == bean) {<br/>            exposedObject = earlySingletonReference;<br/>        }<br/>    }<br/>    try {<br/>        registerDisposableBeanIfNecessary(beanName, bean, mbd);<br/>    } catch (BeanDefinitionValidationException ex) {<br/>        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);<br/>    }<br/>    return exposedObject;<br/>}

populateBean

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {<br/>    PropertyValues pvs = mbd.getPropertyValues();<br/>    if (bw == null) {<br/>        if (!pvs.isEmpty()) {<br/>            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");<br/>        } else {<br/>            return;<br/>        }<br/>    }<br/>    boolean continueWithPropertyPopulation = true;<br/>    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {<br/>        for (BeanPostProcessor bp : getBeanPostProcessors()) {<br/>            if (bp instanceof InstantiationAwareBeanPostProcessor) {<br/>                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;<br/>                if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {<br/>                    continueWithPropertyPopulation = false;<br/>                    break;<br/>                }<br/>            }<br/>        }<br/>    }<br/>    if (!continueWithPropertyPopulation) {<br/>        return;<br/>    }<br/>    if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {<br/>        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);<br/>        autowireByName(beanName, mbd, bw, newPvs);<br/>        pvs = newPvs;<br/>    } else if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {<br/>        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);<br/>        autowireByType(beanName, mbd, bw, newPvs);<br/>        pvs = newPvs;<br/>    }<br/>    boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();<br/>    boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);<br/>    if (hasInstAwareBpps || needsDepCheck) {<br/>        PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);<br/>        if (hasInstAwareBpps) {<br/>            for (BeanPostProcessor bp : getBeanPostProcessors()) {<br/>                if (bp instanceof InstantiationAwareBeanPostProcessor) {<br/>                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;<br/>                    pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);<br/>                    if (pvs == null) {<br/>                        return;<br/>                    }<br/>                }<br/>            }<br/>        }<br/>        if (needsDepCheck) {<br/>            checkDependencies(beanName, mbd, filteredPds, pvs);<br/>        }<br/>    }<br/>    applyPropertyValues(beanName, mbd, bw, pvs);<br/>}

Design Patterns

The following design patterns are employed throughout the Spring container implementation.

Factory Pattern

Intent: Define an interface for creating objects, letting subclasses decide which class to instantiate. Solves the problem of choosing concrete implementations.

Key code: AbstractBeanFactory’s abstract createBean method is implemented by concrete factories.

/**<br/> * Create a bean instance for the given merged bean definition (and arguments).<br/> * @param beanName the name of the bean<br/> * @param mbd the merged bean definition for the bean<br/> * @param args explicit arguments to use for constructor or factory method invocation<br/> * @return a new instance of the bean<br/> */<br/>protected abstract Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException;

Singleton Pattern

Ensures a class has only one instance and provides a global access point. Spring checks the singleton cache before creating a bean.

Object sharedInstance = getSingleton(beanName);<br/>if (sharedInstance != null && args == null) {<br/>    // return cached instance<br/>    bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);<br/>}

Builder Pattern

Spring’s BeanDefinitionBuilder constructs BeanDefinition objects by chaining method calls.

public BeanDefinitionBuilder addConstructorArgValue(Object value) {<br/>    this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(this.constructorArgIndex++, value);<br/>    return this;<br/>}<br/><br/>public BeanDefinitionBuilder addPropertyValue(String name, Object value) {<br/>    this.beanDefinition.getPropertyValues().add(name, value);<br/>    return this;<br/>}

Decorator Pattern

Spring wraps beans with additional functionality (e.g., AOP proxies). The doCreateBean method creates a BeanWrapper that decorates the raw bean instance.

BeanWrapper instanceWrapper = null;<br/>if (mbd.isSingleton()) {<br/>    instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);<br/>}<br/>if (instanceWrapper == null) {<br/>    instanceWrapper = createBeanInstance(beanName, mbd, args);<br/>}

Observer Pattern

Spring’s event system uses ApplicationEventPublisher and ApplicationListener to broadcast events.

protected void publishEvent(Object event, ResolvableType eventType) {<br/>    ApplicationEvent applicationEvent;<br/>    if (event instanceof ApplicationEvent) {<br/>        applicationEvent = (ApplicationEvent) event;<br/>    } else {<br/>        applicationEvent = new PayloadApplicationEvent<>(this, event);<br/>        if (eventType == null) {<br/>            eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();<br/>        }<br/>    }<br/>    if (this.earlyApplicationEvents != null) {<br/>        this.earlyApplicationEvents.add(applicationEvent);<br/>    } else {<br/>        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);<br/>    }<br/>    if (this.parent != null) {<br/>        this.parent.publishEvent(event);<br/>    }<br/>}

Strategy Pattern

Spring delegates bean instantiation to an InstantiationStrategy, allowing different strategies (e.g., CGLIB, simple) to be swapped.

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {<br/>    try {<br/>        Object beanInstance;<br/>        if (System.getSecurityManager() != null) {<br/>            beanInstance = AccessController.doPrivileged(() -> getInstantiationStrategy().instantiate(mbd, beanName, this), getAccessControlContext());<br/>        } else {<br/>            beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);<br/>        }<br/>        BeanWrapper bw = new BeanWrapperImpl(beanInstance);<br/>        initBeanWrapper(bw);<br/>        return bw;<br/>    } catch (Throwable ex) {<br/>        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);<br/>    }<br/>}

Thoughts

Inversion of Control (IoC) is a design principle that decouples object creation from object usage. Dependency Injection (DI) is a concrete implementation of IoC, where the container supplies required dependencies to objects rather than the objects creating them themselves.

Spring’s IoC container acts as a mediator, allowing beans (actors) to live their lifecycle on the stage (ApplicationContext) defined by the core script (Spring core).

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.

Design PatternsIoCspringdependency-injectionBeanFactory
Alibaba Cloud Developer
Written by

Alibaba Cloud Developer

Alibaba's official tech channel, featuring all of its technology innovations.

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.