Backend Development 9 min read

How Spring Solves Circular Dependencies and the Underlying Essence

This article explains how Spring resolves circular dependencies for singleton beans using a three‑level cache, demonstrates a simple prototype implementation that mimics Spring's behavior, and relates the problem to the classic two‑sum algorithm to reveal its fundamental nature.

Java Captain
Java Captain
Java Captain
How Spring Solves Circular Dependencies and the Underlying Essence

Spring's handling of circular dependencies has become a popular Java interview question; the author expresses some skepticism toward framework source‑code questions but proceeds to illustrate how Spring solves the problem and what the core issue really is.

Spring only supports circular dependencies for singleton beans; prototype‑scoped beans do not, and the AbstractBeanFactory throws a BeanCurrentlyInCreationException when a prototype bean references another prototype bean in a cycle.

if (isPrototypeCurrentlyInCreation(beanName)) {
  throw new BeanCurrentlyInCreationException(beanName);
}

For singleton beans Spring maintains three maps— singletonObjects (the fully initialized singleton pool), singletonFactories (factories that can create early references), and earlySingletonObjects (early, partially constructed bean instances). The latter two act as stepping stones that allow Spring to expose a bean reference before the bean is fully initialized, thus breaking the cycle.

The author uses a cup analogy: the two auxiliary maps are like two cups used to pour hot water (the bean) into the final container ( singletonObjects ) after the necessary adjustments.

To expose the essence, the author provides a minimal Java implementation that mimics Spring's behavior: a static cacheMap stores created beans, getBean creates an instance, puts it into the cache, then injects fields recursively, reusing cached instances to resolve circular references.

private static Map
cacheMap = new HashMap<>(2);

public static
T getBean(Class
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;
}

Classes A and B each hold a reference to the other, and the simple container resolves the cycle exactly as Spring does.

public class A { private B b; }
public class B { private A a; }

The author then draws a parallel to the classic "two‑sum" problem from LeetCode, showing that the same hash‑map‑based approach used to find a pair of numbers adding up to a target mirrors Spring's cache‑first strategy for circular dependencies.

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map
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");
    }
}

In conclusion, understanding Spring's three‑level cache and its simple map‑based resolution strategy helps demystify the circular‑dependency mechanism and prevents readers from getting lost in source‑code details.

JavaSpringDependency InjectionBeanCircular DependencyTwo Sum
Java Captain
Written by

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.

0 followers
Reader feedback

How this landed with the community

login 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.