How Spring Solves Circular Dependencies and the Underlying Essence
This article explains how Spring resolves circular dependencies in singleton beans using a three‑level cache, contrasts it with prototype beans, provides a minimal reflective implementation, and draws an analogy to the classic two‑sum algorithm to illustrate the core principle.
Spring's handling of circular dependencies—especially in singleton beans—has become a popular Java interview topic, and this article walks through the mechanism Spring uses and the fundamental idea behind it.
When a prototype bean participates in a circular reference, Spring detects it early in AbstractBeanFactory and throws a BeanCurrentlyInCreationException, as shown in the snippet:
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}For singleton beans, Spring employs a three‑level cache inside DefaultSingletonBeanRegistry:
singletonObjects – the final cache holding fully created singleton instances.
singletonFactories – factories that can create early references to beans.
earlySingletonObjects – temporary storage for early (incomplete) bean references used during wiring.
The two auxiliary caches act as stepping stones, allowing Spring to expose a bean reference before its full initialization is complete, then later replace it with the fully constructed bean.
To illustrate the concept, the author provides a compact Java example that mimics Spring's behavior using a static Map<String, Object> as a cache and reflection to inject fields, handling circular references automatically:
private static Map<String, Object> cacheMap = new HashMap<>(2);
public static void main(String[] args) {
Class[] classes = {A.class, B.class};
for (Class aClass : classes) {
getBean(aClass);
}
System.out.println(getBean(B.class).getA() == getBean(A.class));
System.out.println(getBean(A.class).getB() == getBean(B.class));
}
@SneakyThrows
private static <T> T getBean(Class<T> beanClass) {
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;
}The author then abstracts the essence of circular dependency to the classic "two‑sum" problem: locating two numbers that add up to a target using a single pass and a hash map. The solution mirrors Spring's cache‑first approach:
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");
}
}By first checking the cache (map) for a needed value and then storing the current value, the algorithm demonstrates the same principle as Spring's early‑reference handling.
In conclusion, understanding Spring's three‑level cache and its analogy to simple hash‑map algorithms helps demystify the seemingly complex process of resolving circular dependencies.
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.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
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.
