A cyclic dependency occurs when two or more Spring beans depend on each other directly or indirectly, creating a circular reference. This can lead to bean creation failures, especially when constructor injection is used. The best solution is to redesign the application to reduce tight coupling and establish clear dependency boundaries.
Key Points: • Redesign classes to eliminate circular references and follow the Single Responsibility Principle. • Prefer constructor injection for mandatory dependencies, but use @Lazy only when a circular dependency cannot be avoided. • Introduce interfaces, mediator classes, or event-driven communication to decouple tightly connected components.
Common Solutions:
1. Refactor the Design • Extract shared functionality into a separate service. • Reduce direct dependencies between beans.
2. Use @Lazy • Delays bean initialization until it is actually needed. • Useful when redesigning is not immediately possible.
3. Introduce Interfaces • Decouples implementation details. • Improves maintainability and testability.
4. Use Setter Injection • Allows Spring to create beans first and inject dependencies later. • Can resolve some circular dependency scenarios.
5. Use Event-Based Communication • Replace direct bean references with Spring Events. • Promotes loose coupling.
Example: Suppose OrderService depends on PaymentService and PaymentService depends on OrderService. Instead of directly referencing each other, create a separate TransactionService that coordinates both services.
Code Example:
@Service
public class OrderService {
private final PaymentService paymentService;
public OrderService(
@Lazy PaymentService paymentService) {
this.paymentService = paymentService;
}
}
@Service
public class PaymentService {
private final OrderService orderService;
public PaymentService(
@Lazy OrderService orderService) {
this.orderService = orderService;
}
}Note: Using @Lazy resolves the issue technically, but redesigning the architecture is generally the preferred long-term solution.
Interview Tip: A concise interview answer is: Cyclic dependencies occur when two or more Spring beans depend on each other. The preferred solution is to refactor the design and remove tight coupling by introducing separate services or interfaces. If redesign is not feasible, Spring provides options such as @Lazy, setter injection, or event-driven communication to break the dependency cycle.