Understanding and Resolving Circular Dependencies in Spring Boot
This article explains what circular dependencies are in Spring Boot, why they cause startup failures from version 2.6 onward, and presents several practical solutions—including constructor injection, setter injection, @Lazy, @Autowired(required=false), @DependsOn, and interface segregation—accompanied by code examples.
The article explains what circular dependencies are in Spring Boot applications, where two or more beans depend on each other, forming a cycle that prevents proper initialization.
Before Spring Boot 2.6 the framework automatically resolved such cycles; starting with 2.6 it detects them and throws an error, so the application fails to start.
*************************** APPLICATION FAILED TO START *************************** Description: The dependencies of some of the beans in the application context form a cycle: ┌─────┐ | componentA ↑ ↓ | componentB └─────┘ Action: Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
Typical causes include constructor injection cycles, property injection cycles, and method injection cycles.
Spring Boot provides several ways to break the cycle:
Constructor injection – inject dependencies through constructors instead of fields.
Setter injection – use setter methods to inject dependencies.
Lazy injection – annotate one side with @Lazy to defer its creation.
Set @Autowired(required = false) to make the injection optional.
Use @DependsOn to control bean initialization order.
Below are code examples.
ComponentA (field injection)
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class ComponentA {
@Resource
private ComponentB componentB;
}ComponentB (field injection)
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class ComponentB {
@Resource
private ComponentA componentA;
}With Spring Boot 2.6 this configuration fails with the error shown above.
Constructor‑injection solution
public class A {
private B b;
public A(B b) { this.b = b; }
}
public class B {
private A a;
public B(A a) { this.a = a; }
}@Lazy injection example
@Component
public class A {
private final B b;
public A(@Lazy B b) { this.b = b; }
}
@Component
public class B {
private final A a;
public B(A a) { this.a = a; }
}Interface segregation solution
public interface Service {
void doSomething();
}
public class A {
private final Service service;
public A(Service service) { this.service = service; }
}
public class B {
private final Service service;
public B(Service service) { this.service = service; }
}
@Service
public class ServiceImpl implements Service {
private final A a;
private final B b;
public ServiceImpl(A a, B b) { this.a = a; this.b = b; }
@Override
public void doSomething() { /* ... */ }
}By applying any of these techniques, the circular dependency is eliminated and the Spring Boot application can start normally.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
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.