In this post, we will discuss one of the most asked Spring interview questions. What happens if there are circular dependencies in Spring.
Table of Contents
Problem
What if there are circular dependencies in Spring?
For example: Class A requires an instance of Class B and Class B requires an instance of Class A.
Let’s create an example for the same.
1. Create a simple java maven project.
2. Maven dependency
put spring and cglib maven dependency in pom.xml.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- cglib required for @configuration annotation --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.0</version> </dependency> </dependencies> <properties> <spring.version>4.2.1.RELEASE</spring.version> </properties> |
So your pom.xml will look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"< <modelVersion<4.0.0</modelVersion> <groupId>org.arpit.java2blog</groupId> <artifactId>SpringHelloWorldJavaBasedConfiguration</artifactId> <version>0.0.1-SNAPSHOT</version> <name>SpringHelloWorldJavaBasedConfiguration</name> <description>It provides hello world program for java based config </description> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- cglib required for @configuration annotation --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.0</version> </dependency> </dependencies> <properties> <spring.version>4.2.1.RELEASE</spring.version> </properties> </project> |
3. Create Bean class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
package org.arpit.java2blog; public class A { B b; public A(B b) { super(); this.b = b; } public B getB() { return b; } public void setB(B b) { this.b = b; } public void doSomeThing() { System.out.println("Doing some work"); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package org.arpit.java2blog; public class B { A a; public B(A a) { super(); this.a = a; } public A getA() { return a; } public void setA(A a) { this.a = a; } } |
4. ApplicationContext.xml
Create ApplicationContext.xml in src/main/resources as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?xml version="1.0" encoding="UTF-8"?> <beans default-lazy-init="true" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="a" class="org.arpit.java2blog.A"> <constructor-arg index="0" ref="b"/> </bean> <bean id="b" class="org.arpit.java2blog.B"> <constructor-arg index="0" ref="a"/> </bean> <context:annotation-config/> </beans> |
Please note that we are using Contructor injection to inject dependencies here.
5. Create CircularDependencyMain.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package org.arpit.java2blog; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Circular dependencies in Spring * */ public class CircularDependencyMain { public static void main( String[] args ) { ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml"); A a = (A) ac.getBean("a"); a.doSomeThing(); ac.close(); } } |
6. Run it
When you run above program, you will get below output.
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 11:21:58 IST 2018]; root of context hierarchy
Jun 15, 2018 11:21:58 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [ApplicationContext.xml] Exception in thread “main” org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘a’ defined in class path resource [ApplicationContext.xml]: Cannot resolve reference to bean ‘b’ while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘b’ defined in class path resource [ApplicationContext.xml]: Cannot resolve reference to bean ‘a’ while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘a’: Requested bean is currently in creation: Is there an unresolvable circular reference?
.
.
.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘b’ defined in class path resource [ApplicationContext.xml]: Cannot resolve reference to bean ‘a’ while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘a’: Requested bean is currently in creation: Is there an unresolvable circular reference?
.
.
.
As you can see, we are getting BeanCurrentlyInCreationException.Requested bean is currently in creation: Is there an unresolvable circular reference?
Solution
There are many solution to this problem.
Redesign
Can you redesign your classes to avoid circular dependencies. Circular dependencies are bad and should be avoided if possible at all. There are very rare situations where circular dependencies can not be avoid.
Setter injection
If you use Setter injection instead of Constructor injection to solve Circular dependencies in Spring, there won’t be any issue.
Let’s change the above example and try Setter injection.
Update A.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package org.arpit.java2blog; public class A { B b; public B getB() { return b; } public void setB(B b) { this.b = b; } public void doSomeThing() { System.out.println("Doing some work"); } } |
Update B.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package org.arpit.java2blog; public class B { A a; public A getA() { return a; } public void setA(A a) { this.a = a; } } |
Update ApplicationContext.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="a" class="org.arpit.java2blog.A"> <property name="b" ref="b"/> </bean> <bean id="b" class="org.arpit.java2blog.B"> <property name="a" ref="a"/> </bean> <context:annotation-config/> </beans> |
Run CircularDependencyMain again
Output
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 11:35:26 IST 2018]; root of context hierarchy
Jun 15, 2018 11:35:26 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [ApplicationContext.xml] Doing some work
Jun 15, 2018 11:35:26 AM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 11:35:26 IST 2018]; root of context hierarchy
@Lazy with @Autowired annotation
You use @Lazy with @Autowired to solve circular dependencies in Spring.
Let’s change the above example and try with @Lazy annotation.
Update A.java
Please note that we have used @Autowired and @Lazy annotations here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package org.arpit.java2blog; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; public class A { B b; @Autowired public A(@Lazy B b) { super(); this.b = b; } public B getB() { return b; } public void setB(B b) { this.b = b; } public void doSomeThing() { System.out.println("Doing some work"); } } |
Update B.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package org.arpit.java2blog; import org.springframework.beans.factory.annotation.Autowired; public class B { A a; @Autowired public B(A a) { super(); this.a = a; } public A getA() { return a; } public void setA(A a) { this.a = a; } } |
Update ApplicationContext.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="a" class="org.arpit.java2blog.A"> </bean> <bean id="b" class="org.arpit.java2blog.B"> </bean> <context:annotation-config/> </beans> |
Run CircularDependencyMain again
Output
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 11:35:26 IST 2018]; root of context hierarchy
Jun 15, 2018 11:35:26 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [ApplicationContext.xml] Doing some work
Jun 15, 2018 11:35:26 AM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 11:35:26 IST 2018]; root of context hierarchy
As you can see, program works fine with @Lazy annotation as well.
@PostConstruct with @Autowired annotation
You use @PostConstruct with @Autowired to solve circular dependencies in Spring.
Let’s change the above example and try Setter injection.
Update A.java
Please note that we have used @PostConstruct and @Autowired annotations here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
package org.arpit.java2blog; import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; public class A { @Autowired B b; public B getB() { return b; } public void setB(B b) { this.b = b; } @PostConstruct public void init() { b.setA(this); } public void doSomeThing() { System.out.println("Doing some work"); } } |
Update B.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package org.arpit.java2blog; import org.springframework.beans.factory.annotation.Autowired; public class B { A a; @Autowired public B(A a) { super(); this.a = a; } public A getA() { return a; } public void setA(A a) { this.a = a; } } |
Update ApplicationContext.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="a" class="org.arpit.java2blog.A"> </bean> <bean id="b" class="org.arpit.java2blog.B"> </bean> <context:annotation-config/> </beans> |
Run CircularDependencyMain again
Output
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 11:35:26 IST 2018]; root of context hierarchy
Jun 15, 2018 11:35:26 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [ApplicationContext.xml] Doing some work
Jun 15, 2018 11:35:26 AM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 11:35:26 IST 2018]; root of context hierarchy
As you can see, program works fine with @PostContruct annotation as well.
Thats’s all about Circular dependencies in Spring.
For more questions, refer Spring interview questions.