Mastering Spring’s Circular Dependency: How the Three-Level Cache Solves Bean Loops
This article explains Spring's circular dependency problem, the conditions under which it can be resolved, and how Spring's three‑level cache—combined with AOP proxy handling—enables beans to reference each other without causing creation failures, providing a comprehensive answer for interview scenarios.
Introduction
Spring's circular dependency is a frequent interview topic; understanding its source‑code handling gives you a powerful answer.
The core ideas are:
What is a circular dependency?
When can Spring resolve it?
How does Spring solve it?
The article also corrects two common misconceptions:
Only setter injection can resolve circular dependencies (false).
The three‑level cache is solely for efficiency (false).
What Is Circular Dependency?
It means class A depends on B while B also depends on A, e.g.
Code example:
@Component
public class A {
// A injects B
@Autowired
private B b;
}
@Component
public class B {
// B injects A
@Autowired
private A a;
}Another form is a bean that depends on itself:
@Component
public class A {
// A injects itself
@Autowired
private A a;
}When Can Circular Dependency Be Handled?
Two pre‑conditions must be satisfied:
The involved beans must be singletons.
Not all dependencies may be constructor‑injected; at least one setter (or field) injection is required.
Example where both constructors are used leads to BeanCurrentlyInCreationException:
AB mutual (setter & setter) – resolved.
AB mutual (constructor & constructor) – not resolved.
AB mutual (A uses setter, B uses constructor) – resolved.
AB mutual (A uses constructor, B uses setter) – not resolved.
How Spring Solves Circular Dependency
Spring distinguishes simple circular dependencies (no AOP) and those involving AOP.
Simple Circular Dependency (No AOP)
Bean creation follows three steps: instantiate, populate properties, initialize.
AbstractAutowireCapableBeanFactory.createBeanInstance(...)During instantiation Spring adds a factory to the third‑level cache (singletonFactories). When another bean needs the first bean, Spring retrieves the early reference from the factory, injects it, and later moves the fully created bean to the first‑level cache (singletonObjects).
Circular Dependency with AOP
When a bean is proxied, getEarlyBeanReference returns the proxy instead of the raw instance, ensuring that dependent beans receive the proxied object.
The three‑level cache uses a factory to delay proxy creation until it is actually needed, avoiding unnecessary proxying of beans that are not part of a circular reference.
Does the Three‑Level Cache Improve Efficiency?
Analysis shows that the cache does not provide performance gains; its purpose is to enable correct proxy handling in circular scenarios.
Summary
Spring resolves circular dependencies using a three‑level cache: singletonObjects (first level), earlySingletonObjects (second level), and singletonFactories (third level). The factory supplies an early reference (or proxy) when needed, allowing beans A and B to be wired despite mutual references.
Using only a second‑level cache would force early proxy creation for all beans, violating Spring’s design principle of creating proxies after bean initialization.
Thought Question
Why does the third scenario in the table get resolved while the fourth does not? Because Spring creates beans in natural order, so A is instantiated first, exposing an early reference that B can use.
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.
