Understanding How Spring Handles Circular Dependencies and Its Core Principle

This article explains Spring's three‑level cache mechanism for resolving circular dependencies in singleton beans, demonstrates a simplified implementation with Java code, draws an analogy to the classic two‑sum algorithm, and highlights the essential idea behind circular dependency handling.

Architecture Digest
Architecture Digest
Architecture Digest
Understanding How Spring Handles Circular Dependencies and Its Core Principle

Spring's circular‑dependency problem, a common Java interview question, mainly concerns singleton beans that reference each other through properties. The article first notes that prototype‑scoped beans do not support circular dependencies and will throw a BeanCurrentlyInCreationException.

Spring solves the singleton case by maintaining three internal maps (often called a three‑level cache) in DefaultSingletonBeanRegistry: singletonObjects – the final cache that holds fully created singleton beans. singletonFactories – stores factories capable of creating bean instances. earlySingletonObjects – holds early references to beans that are not yet fully initialized.

During bean creation, Spring first creates an instance and puts it into earlySingletonObjects, then resolves its dependencies using singletonFactories. Once all dependencies are satisfied, the bean is moved to singletonObjects. This “pouring water between cups” process is illustrated with several diagrams in the original article.

To make the concept clearer, the author provides a minimal Java implementation that mimics Spring's behavior. The code creates a static cacheMap to store bean instances, recursively instantiates beans, and injects fields by looking them up in the cache, thereby handling circular references without the full Spring infrastructure:

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

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

private 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;
}

The author then draws an analogy to the classic "two‑sum" problem from LeetCode, showing that the same hashmap‑based approach used to resolve circular dependencies mirrors the algorithm that finds two numbers adding up to a target:

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");
    }
}

Finally, the article concludes that understanding the essence of circular dependency—caching early bean references—helps avoid getting lost in Spring's source code, and encourages readers to implement a simple version first to grasp why Spring adopts such a complex mechanism.

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.

springdesign patterncircular-dependencydependency-injection
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

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.