🚨 Spring Boot cyclic dependency full analysis: principle, solution and best practice
#SpringBoot Core #Dependency Injection #Design Pattern #Performance Optimization
1. The nature and harm of circular dependence
1.1 What is circular dependency?
Circular dependencyIt refers to two or more beans referring directly or indirectly to each other to form a closed-loop dependency relationship.
Typical scenarios:
@Service public class ServiceA { @Autowired private ServiceB serviceB; } @Service public class ServiceB { @Autowired private ServiceA serviceA; }
An exception is thrown when Spring starts:
BeanCurrentlyInCreationException: Error creating bean with name 'serviceA':
Requested bean is currently in creation: Is there an unresolvable circular reference?
1.2 Core hazards
- Application startup failed: Spring cannot complete bean initialization
- Design defect signal: The module responsibilities are unclear and the coupling is too high
- Potential performance issues: Even if circular dependencies are resolved, it may cause hidden initialization order issues
Pring cannot complete bean initializationDesign defect signal: The module responsibilities are unclear and the coupling is too highPotential performance issues: Even if circular dependencies are resolved, it may cause hidden initialization order issues
2. Spring's Level 3 Caching Mechanism
Spring solves through Level 3 cacheSetter/Field InjectionCircular dependency, but cannot be resolvedConstructor injectioncyclic dependency.
2.1 Level 3 cache structure
Cache Level | Store content |
---|---|
SingletonObjects | Fully initialized bean |
Level 2 cache (earlySingletonObjects) | Early exposure of early beans (unfinished attribute fill) |
SingletonFactories | Bean factory object (used to generate early references) |
2.2 Solution process (taking ServiceA and ServiceB as examples)
1. createServiceA → Put the original object factory into the third level cache 2. fillingServiceAproperty → Discover the needServiceB 3. createServiceB → Put the original object factory into the third level cache 4. fillingServiceBproperty → Get from Level 3 cacheServiceAFactory → Generate proxy objects 5. ServiceBInitialization is completed → Move into Level 1 cache 6. ServiceA继续fillingServiceB → Get from Level 1 cacheServiceB → Complete initialization
3. Solution and code combat
3.1 Avoid constructor injection cycles
Constructor injection circular dependencies cannot be resolved(Spring 5.3+ is disabled by default):
// Error example: Start-up failed directly@Service public class ServiceA { private final ServiceB serviceB; public ServiceA(ServiceB serviceB) { = serviceB; } } @Service public class ServiceB { private final ServiceA serviceA; public ServiceB(ServiceA serviceA) { = serviceA; } }
Forced to allow constructor cyclic dependencies (not recommended):
# -circular-references=true
3.2 Injection using Setter/Field
Change constructor injection to Setter injection:
@Service public class ServiceA { private ServiceB serviceB; @Autowired public void setServiceB(ServiceB serviceB) { = serviceB; } } @Service public class ServiceB { private ServiceA serviceA; @Autowired public void setServiceA(ServiceA serviceA) { = serviceA; } }
3.3 @Lazy Lazy Loading
Forced to delay the initialization of one of the beans:
@Service public class ServiceA { @Lazy @Autowired private ServiceB serviceB; }
principle: ServiceB is proxyed and will be actually initialized only when it is called for the first time.
3.4 Abstract decoupling of interfaces
Implement class dependencies through interface isolation:
public interface IServiceA { void doSomething(); } public interface IServiceB { void doAnother(); } @Service public class ServiceAImpl implements IServiceA { @Autowired private IServiceB serviceB; } @Service public class ServiceBImpl implements IServiceB { @Autowired private IServiceA serviceA; }
4. Deep optimization: Design mode application
4.1 Dependency inversion principle (DIP)
High-level modules should not rely on low-level modules, both should rely on abstraction:
// Define the data access interfacepublic interface UserRepository { User findById(Long id); } // High-level service dependency interface@Service public class UserService { private final UserRepository repository; public UserService(UserRepository repository) { = repository; } } // Low-level implementation@Repository public class JpaUserRepository implements UserRepository { // Implementation details}
4.2 Event-driven model
Decoupling of strong dependencies through ApplicationEvent:
// ServiceA release event@Service public class ServiceA { @Autowired private ApplicationEventPublisher publisher; public void triggerEvent() { (new CustomEvent(this)); } } // ServiceB listens to events@Service public class ServiceB { @EventListener public void handleEvent(CustomEvent event) { // Processing logic } }
V. Detection and prevention tools
5.1 IDE detection
-
IntelliJ IDEA: Automatically mark circular dependencies (need to be installed
Spring Assistant
Plugin) -
Eclipse:pass
STS (Spring Tool Suite)
Plugin Tips
5.2 Maven plug-in analysis
<plugin> <groupId></groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId></groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin>
Run the command:
mvn spring-boot:run -=dev
5.3 Architecture Specifications
-
Modular design: Split modules by business (such as
user-service
,order-service
) -
Reliance rules:
- The lower layer module can depend on the upper layer
- The same level prohibits interdependence
- General tools category sinks to
common
Module
6. FAQs
Q1: Will allowing cyclic dependencies have any impact on performance?
- Short-term impact: Increase the context switch when bean creation
- Long-term risk: May cause memory leaks (such as proxy objects not being properly released)
Q2: Can @Lazy annotation be used casually?
- Use caution scenario: Frequently called beans will increase proxy overhead
- Best Practice: Only for solving historical code that cannot be refactored
Q3: Why can Spring solve the problem of Setter injection circular dependencies?
- Core mechanism: Level 3 cache exposes object references that have not been initialized in advance
7. Summary and best practices
Golden Rules:
- Preferred constructor injection: Forced exposure of dependencies
- Comply with the principle of single responsibility: Split classes with more than 500 lines of code
- Regular reliance review: Use ArchUnit and other tools to detect architecture specifications
Emergency repair process:
Discover circular dependencies → Temporary solution with @Lazy → Mark as technical debt → Schedule refactoring
Tool recommendations:
- ArchUnit: Architectural rule detection
- Spring Boot Actuator: Runtime dependency analysis
Through reasonable design + specification constraints, circular dependencies can be effectively avoided and a highly maintainable Spring Boot application can be built! 🚀
This is the article about the full analysis of Spring Boot circular dependencies: principles, solutions and best practices. For more related Spring Boot circular dependencies, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!