How Spring framework resolves circular dependencies?

What Is a Circular Dependency?

A circular dependency occurs when two or more beans depend on each other directly or indirectly.

Example:

@Component
public class A {
    private final B b;
    public A(B b) { this.b = b; }
}

@Component
public class B {
    private final A a;
    public B(A a) { this.a = a; }
}

Here, Spring can’t instantiate A without B, and can’t instantiate B without A → circular reference.

How Spring Tries to Resolve It

1. Using Singleton Beans and Setter/Field Injection

If the beans are singletons and injected via setter or field injection, Spring can usually handle it automatically.

How It Works (Simplified 3-Step Process):

Create bean instance (without dependencies)
Spring creates a raw object (via constructor) and stores a reference to it in an early singleton cache.

Expose the reference early
When another bean needs this bean, Spring uses the early reference from the cache (even if the bean is not fully initialized yet).

Inject dependencies and finish initialization
After all dependencies are injected, Spring finishes the initialization lifecycle.

So the circular reference is broken by partially creating beans first.

Example that works fine:

@Component
public class A {
    @Autowired
    private B b;
}

@Component
public class B {
    @Autowired
    private A a;
}

Works — both A and B are singletons and injected via fields.

2. Fails for Constructor Injection

If both beans use constructor injection, Spring cannot resolve it automatically because it can’t even instantiate the first bean without the second.

Example:

@Component
public class A {
    private final B b;
    public A(B b) { this.b = b; }
}

@Component
public class B {
    private final A a;
    public B(A a) { this.a = a; }
}

Fails with:
BeanCurrentlyInCreationException: Error creating bean with name ‘A’: Requested bean is currently in creation

How to Fix It

Option 1: Use @Lazy on one of the dependencies

@Lazy tells Spring to inject a proxy that resolves the bean only when it’s actually needed.

@Component
public class A {
    private final B b;
    public A(@Lazy B b) { this.b = b; }
}

This defers the creation of B until it’s actually used — breaking the cycle.