Circular Dependencies in Spring


This post is all about how to resolve circular dependency issue in Spring causing UnsatisfiedDependencyException. In simple words when two services depend on each other causes the circular dependency issue.

Circular dependencies are the issue caused during dependency injection when spring-context tries to load objects and one bean depends on another bean. Suppose when Object A & B depends on each other i.e. A depends on B and vice-versa. Spring throws UnsatisfiedDependencyException while creating objects of A and B because A object cannot be created until unless B is created and visa-versa.

Circular Dependencies in Spring

Let’s understand it using the real code example. Create two services ServiceA and ServiceB and try to inject ServiceA into ServiceB and visa-versa as shown in the above picture.

ServiceA.java
package org.websparrow.service;

import org.springframework.stereotype.Service;

@Service
public class ServiceA {

	private ServiceB serviceB;

	public ServiceA(ServiceB serviceB) {

		System.out.println("Calling Service A");

		this.serviceB = serviceB;
	}
}
ServiceB.java
package org.websparrow.service;

import org.springframework.stereotype.Service;

@Service
public class ServiceB {

	private ServiceA serviceA;

	public ServiceB(ServiceA serviceA) {

		System.out.println("Calling Service B");

		this.serviceA = serviceA;
	}
}

To simulate the circular dependency issue, run the below class, and see the console log.

CircularDependenciesTestApp.java
package org.websparrow;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CircularDependenciesTestApp {

	public static void main(String[] args) {
		SpringApplication.run(CircularDependenciesTestApp.class, args);
	}
}

When we execute CircularDependenciesTestApp class it won’t be able to inject the dependencies due to circular dependencies on each other and will throw a checked exception as shown below:

console log
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-05-27 21:22:46.368 ERROR 4480 --- [           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:

┌─────┐
|  serviceA defined in file [F:\sts4-workspace\circular-dependencies-spring\target\classes\org\websparrow\service\ServiceA.class]
↑     ↓
|  serviceB defined in file [F:\sts4-workspace\circular-dependencies-spring\target\classes\org\websparrow\service\ServiceB.class]
└─────┘

How to resolve this issue?

To solve the circular dependency issue, you have two option:

1. Using @Lazy with constructor injection

We can lazily initialize ServiceB bean during constructor injection in order to delay constructing ServiceB bean. Here are the code changes in ServiceA for more clarity:

ServiceA.java
package org.websparrow.service;

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

@Service
public class ServiceA {

	private ServiceB serviceB;

	public ServiceA(@Lazy ServiceB serviceB) {

		System.out.println("Calling Service A");

		this.serviceB = serviceB;
	}
}

If you run the CircularDependenciesTestApp class again, you’ll find the circular dependency issue is solved.

console log
.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.0.RELEASE)

2020-05-27 21:33:22.637  INFO 7156 --- [           main] o.w.CircularDependenciesTestApp          : Starting CircularDependenciesTestApp on Atul-PC with PID 7156 (F:\sts4-workspace\circular-dependencies-spring\target\classes started by Atul in F:\sts4-workspace\circular-dependencies-spring)
2020-05-27 21:33:22.640  INFO 7156 --- [           main] o.w.CircularDependenciesTestApp          : No active profile set, falling back to default profiles: default
Calling Service A
Calling Service B
2020-05-27 21:33:23.251  INFO 7156 --- [           main] o.w.CircularDependenciesTestApp          : Started CircularDependenciesTestApp in 0.98 seconds (JVM running for 1.667)

2. Using @Autowired along with @Lazy annotation

Using @Autowired along with @Lazy annotation for injecting ServiceB in ServiceA. Let’s use these annotations to inject beans and test our application whether it resolves the issue:

ServiceA.java
package org.websparrow.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

@Service
public class ServiceA {

	@Autowired
	@Lazy
	private ServiceB serviceB;

	/*
	public ServiceA(ServiceB serviceB) {
		System.out.println("Calling Service A");
		this.serviceB = serviceB;
	}
	*/
}

Here is the output on the console log when you run CircularDependenciesTestApp class again:

console log
.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.0.RELEASE)

2020-05-27 21:45:07.583  INFO 4036 --- [           main] o.w.CircularDependenciesTestApp          : Starting CircularDependenciesTestApp on Atul-PC with PID 4036 (F:\sts4-workspace\circular-dependencies-spring\target\classes started by Atul in F:\sts4-workspace\circular-dependencies-spring)
2020-05-27 21:45:07.586  INFO 4036 --- [           main] o.w.CircularDependenciesTestApp          : No active profile set, falling back to default profiles: default
Calling Service B
2020-05-27 21:45:08.141  INFO 4036 --- [           main] o.w.CircularDependenciesTestApp          : Started CircularDependenciesTestApp in 0.928 seconds (JVM running for 1.614)

Conclusion

In this tutorial, we have learned what is a circular dependency,  when it occurs in the application and how to resolve it.

References

  1. Dependency injection
  2. Spring Constructor-based Dependency Injection Example
  3. Spring Properties Dependency Injection Example
  4. Spring Setter-based Dependency Injection Example

Similar Posts

About the Author

Pragati Gupta
Experienced senior technical consultant with a demonstrated history of working in Java, Spring, Hibernate, Web Service, JMS, etc. Read all published posts by Pragati Gupta.