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.
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
singletonObjects = new ConcurrentHashMap<>(256);
/** Level 2 cache */
private final Map
earlySingletonObjects = new HashMap<>(16);
/** Level 3 cache */
private final Map
> 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 doGetBean(String name, @Nullable Class
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
政采云技术
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.