Understanding Spring's Circular Dependency Resolution via Three-Level Caches

This article explains how Spring handles common circular dependencies between singleton Beans by employing a three‑level cache (singletonObjects, earlySingletonObjects, singletonFactories), detailing the prerequisites, cache structures, workflow, and key source code snippets that illustrate the underlying mechanism.

政采云技术
政采云技术
政采云技术
Understanding Spring's Circular Dependency Resolution via Three-Level Caches

1. Introduction

In everyday development, circular dependencies between Beans are common; Spring handles them transparently. This article explains how Spring achieves this.

2. Overview of Circular Dependency

2.1 What is Circular Dependency

Circular dependency occurs when two or more objects reference each other, forming a loop, as shown in the diagram.

public class PersonA {
  @Autowired
  private PersonB personB;
}
public class PersonB {
  @Autowired
  private PersonA personA;
}

2.2 Preconditions for Spring to Resolve Circular Dependency

1. The mutually dependent Beans must be singletons.

2. Dependency injection cannot rely solely on constructor injection, because constructors would cause deadlock.

3. Three-Level Cache Mechanism

3.1 What is the Three-Level Cache

Spring uses a three‑level cache (singletonObjects, earlySingletonObjects, singletonFactories) to manage Bean creation stages.

SingletonObjects (Level 1)

Stores fully instantiated and initialized Beans.

EarlySingletonObjects (Level 2)

Stores early‑exposed Bean instances whose lifecycle is not yet complete.

SingletonFactories (Level 3)

Stores factories that can produce early Bean references via getObject(). When another Bean needs the early Bean, Spring uses the factory to create the instance and later moves it to Level 1.

/** Level 1 cache */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Level 2 cache */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Level 3 cache */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

3.2 Three-Level Cache Workflow

3.3 Source Code Analysis of the Three-Level Cache

Bean Creation in AbstractBeanFactory.doGetBean

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType,
        @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
    // obtain canonical bean name
    String beanName = transformedBeanName(name);
    Object bean;
    // retrieve from caches
    Object sharedInstance = getSingleton(beanName);
    // ... omitted for brevity ...
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    // create singleton if necessary
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
            try {
                return createBean(beanName, mbd, args);
            } catch (BeansException ex) {
                destroySingleton(beanName);
                throw ex;
            }
        });
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
    return (T) bean;
}

Key Method getSingleton()

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // check Level 1
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // check Level 2
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // check Level 3
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

Adding and Cleaning Caches

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

4. Summary

Spring resolves circular dependencies using a three‑level cache: Level 1 (SingletonObjects) holds fully created Beans, Level 2 (EarlySingletonObjects) holds partially created Beans, and Level 3 (SingletonFactories) provides factories to create early references, enabling the container to break circular references.

References

https://www.51cto.com/article/702892.html

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.

Backend Developmentspringbeancircular-dependencyThree-level Cache
政采云技术
Written by

政采云技术

ZCY Technology Team (Zero), based in Hangzhou, is a growth-oriented team passionate about technology and craftsmanship. With around 500 members, we are building comprehensive engineering, project management, and talent development systems. We are committed to innovation and creating a cloud service ecosystem for government and enterprise procurement. We look forward to your joining us.

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.