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:
-
A
DependenceB
,B
DependenceA
。
-
-
Indirect cyclic dependency:
-
A
DependenceB
,B
DependenceC
,andC
Rely 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
@Lazy
Annotations 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@Lazy
annotation:
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 introducingHelper
,BeanA
andBeanB
No 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 providesObjectFactory
andProvider
Interface 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@DependsOn
Specifies 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
@Lazy
Lazy loading. - Refactor the code to avoid direct circular dependencies.
- Use Setter to inject or
@PostConstruct
。 - use
ObjectFactory
orProvider
Perform delayed injection. - use
@DependsOn
Clear loading order.
Recommended plan:
If the code can be refactored,Eliminating circular dependencies is a best practice. When necessary, combine@Lazy
orObjectFactory
to 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!