Backend Development 9 min read

Resolving Circular Dependencies in Spring: Constructor vs Setter Injection and Bean Scopes

This article explains how Spring handles circular dependencies through constructor injection errors, demonstrates how setter injection with singleton scope avoids the issue, and shows why prototype‑scoped beans still fail, providing code examples and internal mechanism details.

Java Captain
Java Captain
Java Captain
Resolving Circular Dependencies in Spring: Constructor vs Setter Injection and Bean Scopes

Introduction: A circular dependency occurs when N classes reference each other in a loop, and creating such objects with new leads to infinite recursion and eventually an out‑of‑memory error. The article examines how Spring resolves (or fails to resolve) these circular references.

1. Constructor‑parameter circular dependency

Spring places every bean that is currently being created into a "currently‑creating bean pool". If, during creation, a bean is found already in this pool, Spring throws BeanCurrentlyInCreationException to indicate an unresolvable circular reference.

Example classes:

public class StudentA {
    private StudentB studentB;
    public void setStudentB(StudentB studentB) { this.studentB = studentB; }
    public StudentA() {}
    public StudentA(StudentB studentB) { this.studentB = studentB; }
}

public class StudentB {
    private StudentC studentC;
    public void setStudentC(StudentC studentC) { this.studentC = studentC; }
    public StudentB() {}
    public StudentB(StudentC studentC) { this.studentC = studentC; }
}

public class StudentC {
    private StudentA studentA;
    public void setStudentA(StudentA studentA) { this.studentA = studentA; }
    public StudentC() {}
    public StudentC(StudentA studentA) { this.studentA = studentA; }
}

XML configuration using constructor arguments:

Running the test class results in:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

2. Setter injection with singleton scope (default)

Spring first instantiates the bean objects, then sets their properties. By using setter injection, the already‑instantiated singleton objects are placed in a cache, allowing Spring to resolve references without entering the creation pool.

Updated XML configuration (setter injection):

Test class prints a normal bean reference, e.g., com.zfx.student.StudentA@1fbfd6 , showing that the circular dependency is resolved.

The underlying mechanism is illustrated in Spring's DefaultSingletonBeanRegistry where early singleton objects are cached to break the cycle:

/** Cache of singleton objects: bean name -> bean instance */
private final Map
singletonObjects = new ConcurrentHashMap<>(64);
/** Cache of singleton factories: bean name -> ObjectFactory */
private final Map
singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name -> bean instance */
private final Map
earlySingletonObjects = new HashMap<>(16);
/** Set of registered singletons in registration order */
private final Set
registeredSingletons = new LinkedHashSet<>(64);
protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

3. Setter injection with prototype scope

Changing the bean scope to prototype means a new instance is created on each request, and Spring does not cache these beans. Consequently, the container cannot expose an early reference for a bean that is still being created, leading to the same circular‑dependency exception.

Prototype‑scoped XML configuration:

Running the same test now produces the same BeanCurrentlyInCreationException because prototype beans cannot be resolved early.

Conclusion: Constructor injection cannot handle circular references, setter injection works with singleton beans by leveraging Spring's early singleton cache, but prototype beans still fail because they are not cached during creation.

(End)

SpringConstructor InjectionSetter InjectionCircular DependencysingletonPrototypeBean Scope
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.