Each service in the service layer is probably having other services, which will be injected using @Autowire. When the services count starts growing, a circular dependency occurs. It does not have to indicate the design problem. It's enough that the central service, which is autowired in many services, consuming one of the other services, the circular dependency will likely arise.
The circular dependency will cause Spring Application Context to fail and the symptom is an error which is a circular dependency. It occurs when a bean A1 depends on another bean B1, and bean B1 depends on the bean A1 as well.
For dealing with the circular dependency in the spring we are having the following possible solutions.
1> Redesign
2> Use @Lazy
3> Use Setter/Field Injection
4> Use @PostConstruct
5> Implement ApplicationContextAware and InitializingBean
1> Redesign :-
When we have a circular dependency, it’s you have a design problem and the responsibilities are not well separated. we should have to redesign the components properly so that the hierarchy is well designed and no chance for circular dependencies.
2> Use @Lazy :-
The simplest path to break the cycle is saying Spring to initialize one of the beans lazily. Instead of fully initializing the bean, it will create a proxy to inject it into another bean. The injected bean will only be fully created when it is first needed.
@Component
public class A {
private B b;
@Autowired
public A(@Lazy B b) {
this.b = b;
}
}
3> Use Setter/Field Injection :-
One of the most popular workarounds, also what Spring documentation proposes, is using setter injection.
Simply put if change the ways your beans are wired to use the setter injection (or field injection) instead of the constructor injection – that does address the problem. This way Spring creates beans, but the dependencies are not injected until they are needed.
@Component
public class A {
private B b;
@Autowired
public void setB( b) {
this.b = b;
}
public B getB() {
return b;
}
}
@Component
public class B {
private A a;
private String message = "Hello guys";
@Autowired
public void setA(A a) {
this.a = a;
}
public String getMessage() {
return message;
}
}
4> Use @PostConstruct :-
Another way to break the cycle is injecting a dependency using the @Autowired on one of the beans, and then use the method annotated with the @PostConstruct to set another dependency.
@Component
public class A {
@Autowired
private B b;
@PostConstruct
public void init() {
b.a(this);
}
public B b() {
return b;
}
}
@Component
public class B {
private A a;
private String message = "Hello guys";
public void setA(A a) {
this.a = a;
}
public String getMessage() {
return message;
}
}
5> Implement ApplicationContextAware and InitializingBean :-
If one of the beans implements ApplicationContextAware, the bean has access to Spring context and can extract another bean from there. Implementing InitializingBean we indicate that the bean has to do some actions after all its properties have been set in the case, we want to manually set the dependency.
@Component
public class A implements ApplicationContextAware, InitializingBean {
private B b;
private ApplicationContext context;
public B getB() {
return b;
}
@Override
public void afterPropertiesSet() throws Exception {
circB = context.getBean(B.class);
}
@Override
public void setApplicationContext(final ApplicationContext ctx) throws BeansException {
context = ctx;
}
}
@Component
public class B {
private A a;
private String message = "Hello guys";
@Autowired
public void setA(A a) {
this.a = a;
}
public String getMessage() {
return message;
}
}