April 12, 2022

Circular Dependency in Spring

In this post we’ll see what is circular dependency in Spring and how to resolve circular dependencies.

Circular dependency in Spring

If you mostly use constructor dependency injection, it is possible to create an unresolvable circular dependency scenario. For example; Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. In such configuration where beans for classes A and B are to be injected into each other, the Spring IoC container can’t decide which bean should be created first and throws BeanCurrentlyInCreationException at runtime when it detects this circular reference.

Circular Dependency Spring

Circular dependency Spring example

Here is an example showing how circular dependency may occur in your application.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ClassA {
  private ClassB b;
  @Autowired
  ClassA(ClassB b){
    this.b = b;
  }
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ClassB {
  private ClassA a;
  @Autowired
  ClassB(ClassA a){
    this.a = a;
  }
}

There are two classes ClassA and ClassB, ClassA has a dependency on ClassB and ClassB has a dependency on ClassA and theses dependencies are injected as constructor dependencies.

Following Spring XML configuration sets up the component scanning.

<?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.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd">
          
  <context:component-scan base-package="com.knpcode.springproject.service"/>
</beans>

You can use the following class with main method to read the configuration and run the example.

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
  public static void main( String[] args ){
    // create context using configuration
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml");
  }
}

On running the example program you will get an exception similar to as given below because of the circular reference.

11:08:12.031 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'classB'
11:08:12.039 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'classA'
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'classA' defined in file [F:\knpcode\Spring WorkSpace\SpringProject\target\classes\com\knpcode\springproject\service\ClassA.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'classB' defined in file [F:\knpcode\Spring WorkSpace\SpringProject\target\classes\com\knpcode\springproject\service\ClassB.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classA': Requested bean is currently in creation: Is there an unresolvable circular reference?
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:769)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:218)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1341)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1187)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:845)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85)
	at com.knpcode.springproject.App.main(App.java:10)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'classB' defined in file [F:\knpcode\Spring WorkSpace\SpringProject\target\classes\com\knpcode\springproject\service\ClassB.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classA': Requested bean is currently in creation: Is there an unresolvable circular reference?
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:769)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:218)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1341)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1187)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1251)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1171)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760)
	... 15 more

Resolving circular dependency

In many cases by redesigning classes you can avoid circular dependencies. If you have to have such object dependencies then with in Spring to avoid circular dependencies you can use one of the following solutions.

1. Use setter injection instead of constructor injection. When setter injection is used dependencies are injected only when they are used not at the bean creation time.

@Component
public class ClassA {
  private ClassB b;
  @Autowired
  public void setB(ClassB b) {
    this.b = b;
  }
  public void display() {
    System.out.println("In method of classA");
  }
}
@Component
public class ClassB {
  private ClassA a;
  @Autowired
  public void setA(ClassA a) {
    this.a = a;
  }

  public void display() {
    System.out.println("In method of classB");
  }
}

As you can see now the dependencies are injected as setter dependencies.

On running the application

public class App {
  public static void main( String[] args ){
    // create context using configuration
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml");
    ClassA a = context.getBean("classA", ClassA.class);
    a.display();  	
    context.close();
  }
}
Output
:24:06.884 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'classA'
11:24:07.019 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'classB'
11:24:07.038 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'employeeService'
In method of classA

2. Another way to avoid circular dependency in Spring is to lazy initialize one of the bean. That way even with constructor injection bean is fully initialized only when it is needed.

Here is updated classA where @Lazy is also used with @Autowired, it leads to the creation of a lazy-resolution proxy for the dependencies.

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

@Component
public class ClassA {
  private ClassB b;
  @Autowired
  @Lazy
  ClassA(ClassB b){
    this.b = b;
  }
  public void display() {
    System.out.println("In method of classA");
  }
}

That's all for the topic Circular Dependency in Spring. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment