SoFunction
Updated on 2025-04-14

Springboot circular dependency problem case code and solution

In Spring Boot, if there is a circular dependency between two or more beans (i.e., Bean A depends on Bean B, which in turn depends on Bean A), it will cause Spring's dependency injection mechanism to not be handled correctly, thus throwing an exception. The following are scenario analysis, case code and common solutions for circular dependencies.

1. What is circular dependency?

concept

Circular dependence refers to the interdependence of two or more beans to form a closed loop. For example:

  • Direct cyclic dependency
    • ADependenceBBDependenceA
  • Indirect cyclic dependency
    • ADependenceBBDependenceC,andCRely onA

2. Circular dependency scenario case

Here is a simple direct loop dependency scenario.

Case code Bean A

@Component
public class BeanA {
    @Autowired
    private BeanB beanB;
    public BeanA() {
        ("BeanA Constructor");
    }
    public void doSomething() {
        ("BeanA is doing something");
    }
}

Bean B

@Component
public class BeanB {
    @Autowired
    private BeanA beanA;
    public BeanB() {
        ("BeanB Constructor");
    }
    public void doSomething() {
        ("BeanB is doing something");
    }
}

Startup class

@SpringBootApplication
public class CircularDependencyDemoApplication {
    public static void main(String[] args) {
        (, args);
    }
}

Running results

When running, Spring throws the following exception:

Caused by: :
Error creating bean with name 'beanA':
Requested bean is currently in creation: Is there an unresolvable circular reference?

3. Common ways to solve circular dependencies

Method 1: Use @Lazy annotation

@LazyAnnotations can delay loading of beans, allowing Spring to inject dependencies only when it really needs to be used, thus breaking the circular dependency.

Modify the code

Add on one of the dependencies@Lazyannotation:

BeanA:

@Component
public class BeanA {
    @Autowired
    @Lazy
    private BeanB beanB;
    public BeanA() {
        ("BeanA Constructor");
    }
    public void doSomething() {
        ("BeanA is doing something");
    }
}

BeanB:

@Component
public class BeanB {
    @Autowired
    private BeanA beanA;
    public BeanB() {
        ("BeanB Constructor");
    }
    public void doSomething() {
        ("BeanB is doing something");
    }
}

Running results

The program runs normally, output:

BeanA Constructor
BeanB Constructor

Method 2: Use constructor injection to resolve circular dependencies

Spring cannot resolve circular dependencies through constructor injection because during constructor injection, all dependencies must be fully injected at instantiation. In this case, the code structure needs to be redesigned to break the circular dependency.

Refactoring the code

Refactor circular dependencies into one-way dependencies. For example, third-party collaborator Beans can be introduced to decouple.

BeanA:

@Component
public class BeanA {
    private final Helper helper;
    public BeanA(Helper helper) {
         = helper;
        ("BeanA Constructor");
    }
    public void doSomething() {
        ("BeanA is doing something");
    }
}

BeanB:

@Component
public class BeanB {
    private final Helper helper;
    public BeanB(Helper helper) {
         = helper;
        ("BeanB Constructor");
    }
    public void doSomething() {
        ("BeanB is doing something");
    }
}

Helper:

@Component
public class Helper {
    public void assist() {
        ("Helper is assisting");
    }
}

Running results

Helper Constructor
BeanA Constructor
BeanB Constructor

By introducingHelperBeanAandBeanBNo longer directly depend on each other, circular dependencies are eliminated.

Method 3: Inject using @PostConstruct or Setter

Injection through the constructor can cause circular dependency problems, but you can use Setter method injection to delay the timing of dependency injection.

Modify the code

BeanA:

@Component
public class BeanA {
    private BeanB beanB;
    public BeanA() {
        ("BeanA Constructor");
    }
    @Autowired
    public void setBeanB(BeanB beanB) {
         = beanB;
    }
    public void doSomething() {
        ("BeanA is doing something");
    }
}

BeanB:

@Component
public class BeanB {
    private BeanA beanA;
    public BeanB() {
        ("BeanB Constructor");
    }
    @Autowired
    public void setBeanA(BeanA beanA) {
         = beanA;
    }
    public void doSomething() {
        ("BeanB is doing something");
    }
}

Running results

BeanA Constructor
BeanB Constructor

Setter injection allows Spring to set dependencies after instantiating a bean, thus avoiding circular dependencies.

Method 4: Use ObjectFactory or ProviderPerform delayed injection

Spring providesObjectFactoryandProviderInterface for delaying acquisition of beans, thus avoiding circular dependencies.

Modify the code BeanA:

@Component
public class BeanA {
    private final ObjectFactory<BeanB> beanBFactory;
    public BeanA(ObjectFactory<BeanB> beanBFactory) {
         = beanBFactory;
        ("BeanA Constructor");
    }
    public void doSomething() {
        BeanB beanB = ();
        ("BeanA is doing something with " + beanB);
    }
}

BeanB:

@Component
public class BeanB {
    private final BeanA beanA;
    @Autowired
    public BeanB(BeanA beanA) {
         = beanA;
        ("BeanB Constructor");
    }
    public void doSomething() {
        ("BeanB is doing something");
    }
}

Running results

BeanA Constructor
BeanB Constructor

passObjectFactory,BeanA can dynamically obtain BeanB when needed, avoiding circular dependencies.

Method 5: Use @DependsOn to explicitly load the order

If the circular dependency is due to the loading order of the bean, you can use it@DependsOnSpecifies the loading order.

Modify the code

BeanA:

@Component
@DependsOn("beanB") // Specify that the BeanB should be loaded firstpublic class BeanA {
    @Autowired
    private BeanB beanB;
    public BeanA() {
        ("BeanA Constructor");
    }
    public void doSomething() {
        ("BeanA is doing something");
    }
}

BeanB:

@Component
public class BeanB {
    @Autowired
    private BeanA beanA;
    public BeanB() {
        ("BeanB Constructor");
    }
    public void doSomething() {
        ("BeanB is doing something");
    }
}

Running results

BeanB Constructor
BeanA Constructor

4. Summary

Circular dependencies are common problems, but they can be solved in a number of ways:

  • use@LazyLazy loading.
  • Refactor the code to avoid direct circular dependencies.
  • Use Setter to inject or@PostConstruct
  • useObjectFactoryorProviderPerform delayed injection.
  • use@DependsOnClear loading order.

Recommended plan

If the code can be refactored,Eliminating circular dependencies is a best practice. When necessary, combine@LazyorObjectFactoryto solve the circular dependency problem.

This is the end of this article about springboot circular dependency problems and solutions. For more related springboot circular dependency content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!