How to Resolve Circular Dependency in Spring Boot.
In this blog post we will see various ways to resolve circular dependency while working with Spring Boot application.
Let’s look at the one example of circular dependency .
@Component
public class A {
private B b;
public A( B b) {
this.b = b;
}
}
Code language: Java (java)
@Component
public class B {
private A a;
public B(A a){
this.a = a;
}
}
Code language: Java (java)
If you start the application, you will see following error in your startup log.
2023-02-20T23:59:19.674+13:00 WARN 14376 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'a' defined in file [D:\IdeaProjects\sb-circular-dependency\target\classes\dev\fullstackcode\sb\component\A.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'b' defined in file [D:\IdeaProjects\sb-circular-dependency\target\classes\dev\fullstackcode\sb\component\B.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
2023-02-20T23:59:19.679+13:00 INFO 14376 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2023-02-20T23:59:19.698+13:00 INFO 14376 --- [ main] .s.b.a.l.ConditionEvaluationReportLogger :
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2023-02-20T23:59:19.731+13:00 ERROR 14376 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| a defined in file [D:\IdeaProjects\sb-circular-dependency\target\classes\dev\fullstackcode\sb\component\A.class]
↑ ↓
| b defined in file [D:\IdeaProjects\sb-circular-dependency\target\classes\dev\fullstackcode\sb\component\B.class]
└─────┘
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.
Process finished with exit code 1
Code language: Java (java)
Now look at the solutions to to resolve the circular dependency issue.
Use @Lazy Annotation
If you do not want to change any existing code you can use @Lazy annotation on one of the construction injections.
@Component
public class A {
private B b;
public A( B b) {
this.b = b;
}
}
Code language: Java (java)
@Component
public class B {
private A a;
public B(@Lazy A a){
this.a = a;
}
}
Code language: Java (java)
Use setter Injections
Instead of using the constructor injections, you can use setter injection and se
@Component
public class A {
@Autowired
private B b;
}
Code language: Java (java)
@Component
public class B {
@Autowired
private A a;
}
Code language: Java (java)
With setter injection, you also need to use following property in the application.properties file.
spring.main.allow-circular-references=true
Code language: Java (java)
User @PostConstruct method
You can refactor the code to set the bean from the method with @PostConstruction annotation.
@Component
public class A {
private B b;
public A(B b) {
this.b = b;
}
@PostConstruct
public void init() {
b.setA(this);
}
}
Code language: Java (java)
@Component
public class B {
private A a;
public void setA(A a){
this.a = a;
}
}
Code language: Java (java)
Using afterPropertiesSet method
You can refactor the code to get the bean from application context and set the bean from afterPropertiesSet
method.
@Component
public class A implements ApplicationContextAware, InitializingBean {
private B b;
private ApplicationContext ctx;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
this.b = ctx.getBean(B.class);
this.b.setA(this);
}
}
Code language: Java (java)
@Component
public class B {
private A a;
public void setA(A a) {
this.a = a;
}
}
Code language: Java (java)