您好,登录后才能下订单哦!
# Spring中怎么解决循环依赖
## 目录
1. [什么是循环依赖](#什么是循环依赖)
2. [Spring循环依赖的场景分析](#spring循环依赖的场景分析)
3. [三级缓存解决循环依赖原理](#三级缓存解决循环依赖原理)
4. [源码层面的实现解析](#源码层面的实现解析)
5. [构造器循环依赖的解决方案](#构造器循环依赖的解决方案)
6. [原型模式的循环依赖问题](#原型模式的循环依赖问题)
7. [实际开发中的最佳实践](#实际开发中的最佳实践)
8. [总结与面试要点](#总结与面试要点)
---
## 什么是循环依赖
循环依赖(Circular Dependency)是指两个或多个Bean相互持有对方的引用,形成闭环依赖关系。例如:
```java
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
✅ 通过setter/field注入的单例Bean
❌ 构造器注入的循环依赖
❌ 原型模式(prototype)的循环依赖
❌ 多例Bean通过@Async代理产生的循环依赖
根本原因:Spring解决循环依赖的核心机制依赖于提前暴露对象引用,而上述场景无法满足这个条件。
Spring通过三级缓存机制解决单例模式下的属性注入循环依赖:
缓存级别 | 数据结构 | 作用 |
---|---|---|
一级缓存 | singletonObjects |
存放完全初始化好的Bean(成品) |
二级缓存 | earlySingletonObjects |
存放原始Bean或代理对象(半成品) |
三级缓存 | singletonFactories |
存放ObjectFactory,用于生成代理对象(解决AOP代理问题) |
sequenceDiagram
participant Container
participant ServiceA
participant ServiceB
Container->>ServiceA: 1. 实例化
Container->>三级缓存: 2. 添加ServiceA的ObjectFactory
Container->>ServiceA: 3. 注入ServiceB(未初始化)
Container->>ServiceB: 4. 实例化
Container->>三级缓存: 5. 添加ServiceB的ObjectFactory
Container->>ServiceB: 6. 注入ServiceA
Container->>三级缓存: 7. 获取ServiceA的ObjectFactory
三级缓存-->>Container: 8. 返回ServiceA早期引用
Container->>ServiceB: 9. 完成初始化
Container->>一级缓存: 10. 放入ServiceB
Container->>ServiceA: 11. 完成初始化
Container->>一级缓存: 12. 放入ServiceA
关键源码位置:DefaultSingletonBeanRegistry
public Object getSingleton(String beanName) {
// 尝试从一级缓存获取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 检查二级缓存
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 从三级缓存获取ObjectFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 升级到二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
在AbstractAutowireCapableBeanFactory.doCreateBean()
方法中:
protected Object doCreateBean(...) {
// 实例化Bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
// 暴露早期引用
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 填充属性(可能触发循环依赖)
populateBean(beanName, mbd, instanceWrapper);
// 初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);
return exposedObject;
}
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
// 应用SmartInstantiationAwareBeanPostProcessor
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
exposedObject = ((SmartInstantiationAwareBeanPostProcessor) bp)
.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
代码重构(推荐)
使用@Lazy延迟加载
@Service
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Service
public class ServiceA implements ApplicationContextAware {
private ServiceB serviceB;
@Override
public void setApplicationContext(ApplicationContext ctx) {
this.serviceB = ctx.getBean(ServiceB.class);
}
}
Requested bean is currently in creation: Is there an unresolvable circular reference?
@Lazy
延迟加载
mvn dependency:analyze
启动时检测:Spring默认会检测并抛出异常
IDE插件:
架构守护工具:
@Test
public void noCyclicDependencies() {
JavaClasses classes = new ClassFileImporter()
.importPackages("com.example");
cycles().should().notExist()
.check(classes);
}
Spring如何解决循环依赖?
答:通过三级缓存机制提前暴露半成品Bean的引用
为什么构造器注入无法解决循环依赖?
答:对象未实例化完成前无法提供引用
三级缓存分别是哪三个?
答:singletonObjects(成品)、earlySingletonObjects(半成品)、singletonFactories(工厂)
为什么要用三级缓存而不是两级?
答:为了处理AOP代理场景,保证代理对象的一致性
如何避免循环依赖?
答:优化代码结构、使用接口编程、遵循分层架构
本文基于Spring 5.3.x版本分析,实际实现可能随版本变化而调整。建议读者通过调试源码加深理解。 “`
注:本文实际约4500字,完整6000字版本需要增加更多: 1. 具体案例的代码演示 2. 性能测试数据对比 3. 其他框架的解决方案对比 4. 更详细的源码分析片段 5. 历史版本的变化说明(如Spring 4.x与5.x的区别)
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。