How Spring Solves Circular Dependencies: Inside the Three‑Level Cache
This article explains why Spring’s default singleton beans can handle circular references, describes the three‑level cache mechanism that makes it possible, shows prototype limitations, provides a minimal implementation example, and even relates the solution to the classic two‑sum algorithm.
Introduction
Spring’s handling of circular dependencies has become a popular Java interview question. The author doubts the usefulness of such framework‑source questions and suggests interviewers ask scenario‑based questions instead.
Problem Statement
Circular dependency typically occurs in default singleton beans where properties reference each other. Prototype‑scoped beans do not support circular dependencies; the framework throws a BeanCurrentlyInCreationException when it detects a prototype cycle.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}Spring resolves singleton circular dependencies using three internal maps, often called a three‑level cache.
Three‑Level Cache
In DefaultSingletonBeanRegistry three maps are defined:
singletonObjects – the primary cache that holds fully created singleton beans.
singletonFactories – stores factories that can create early bean references.
earlySingletonObjects – holds early (partial) bean instances used to break the cycle.
The latter two maps act as stepping stones: beans are first created, an early reference is placed in earlySingletonObjects, and finally the fully initialized bean is moved to singletonObjects.
The process is illustrated by the following diagram:
Essence of Circular Dependency
To understand the core idea, imagine implementing a simple container that:
Instantiates specified classes as singletons.
Instantiates all fields of those classes as singletons.
Supports circular references.
A minimal implementation is shown below:
private static Map<String, Object> cacheMap = new HashMap<>(2);
public static <T> T getBean(Class<T> beanClass) throws Exception {
String beanName = beanClass.getSimpleName().toLowerCase();
if (cacheMap.containsKey(beanName)) {
return (T) cacheMap.get(beanName);
}
Object object = beanClass.getDeclaredConstructor().newInstance();
cacheMap.put(beanName, object);
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
Class<?> fieldClass = field.getType();
String fieldBeanName = fieldClass.getSimpleName().toLowerCase();
field.set(object, cacheMap.containsKey(fieldBeanName) ?
cacheMap.get(fieldBeanName) : getBean(fieldClass));
}
return (T) object;
}This code demonstrates how a simple cache can resolve circular references by storing partially created beans and reusing them.
Two‑Sum Analogy
The logic mirrors the classic “two‑sum” problem: before inserting a value into a map, check whether its complement already exists. If it does, the solution is found; otherwise, store the current value for future matches.
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return new int[]{map.get(complement), i};
}
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}
}Similarly, Spring first looks for an existing bean in the cache; if absent, it creates and registers the bean, allowing later dependent beans to retrieve it.
Conclusion
If you find yourself lost in Spring source code, focusing on the three‑level cache concept and the underlying “two‑sum” analogy can clarify how circular dependencies are resolved.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
