您好,登录后才能下订单哦!
# Spring解决循环依赖的方法
## 引言
在面向对象的软件设计中,循环依赖(Circular Dependency)是指两个或多个组件相互直接或间接依赖,形成闭环关系。这种设计在Spring框架管理的Bean之间尤为常见。Spring作为企业级Java开发的核心框架,其依赖注入(DI)机制虽然强大,但循环依赖处理不当会导致应用启动失败或运行时异常。
本文将深入剖析Spring框架解决循环依赖的底层机制,包括三级缓存设计、代理对象处理等关键技术,并结合源码和实际场景分析解决方案的局限性。
---
## 一、循环依赖的典型场景与问题
### 1.1 什么是循环依赖
```java
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
上述代码展示典型的双向依赖:ServiceA依赖ServiceB,同时ServiceB又反向依赖ServiceA。
BeanCurrentlyInCreationException
Spring通过三级缓存解决单例Bean的属性注入循环依赖问题:
缓存级别 | 存储内容 | 源码字段 |
---|---|---|
一级缓存 | 完全初始化的Bean | singletonObjects |
二级缓存 | 早期暴露的原始对象(未填充属性) | earlySingletonObjects |
三级缓存 | 对象工厂(用于生成代理对象) | singletonFactories |
以ServiceA和ServiceB为例:
// 简化版AbstractAutowireCapableBeanFactory
protected Object doCreateBean(...) {
// 1. 实例化
Object bean = createBeanInstance(...);
// 2. 加入三级缓存(关键步骤)
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, bean));
// 3. 属性填充(可能触发循环依赖)
populateBean(beanName, mbd, instanceWrapper);
// 4. 初始化
initializedBean = initializeBean(beanName, exposedObject, mbd);
return initializedBean;
}
当存在AOP代理时,Spring通过SmartInstantiationAwareBeanPostProcessor
确保返回代理对象:
// AbstractAutoProxyCreator
public Object getEarlyBeanReference(Object bean, String beanName) {
return wrapIfNecessary(bean, beanName);
}
@Service
public class ServiceC {
private final ServiceD serviceD;
public ServiceC(ServiceD serviceD) { this.serviceD = serviceD; }
}
@Service
public class ServiceD {
private final ServiceC serviceC;
public ServiceD(ServiceC serviceC) { this.serviceC = serviceC; }
}
这种场景无法解决,因为: - 必须完成构造才能放入缓存 - 双方都卡在构造阶段形成死锁
方案 | 适用场景 | 实现方式 |
---|---|---|
属性注入 | 大多数情况 | @Autowired 字段/Setter |
@Lazy 注解 |
需要延迟初始化 | 在依赖项上添加@Lazy |
接口分离 | 设计层面解耦 | 提取公共接口 |
ApplicationContextAware | 需要手动获取Bean | 实现接口后调用getBean() |
@DependsOn
强制指定顺序(掩盖设计问题)classDiagram
class DefaultSingletonBeanRegistry {
+Map<String,Object> singletonObjects
+Map<String,Object> earlySingletonObjects
+Map<String,ObjectFactory<?>> singletonFactories
+getSingleton(String beanName)
}
class AbstractAutowireCapableBeanFactory {
+doCreateBean()
+createBeanInstance()
+addSingletonFactory()
}
DefaultSingletonBeanRegistry <|-- AbstractAutowireCapableBeanFactory
getBean()
→ doGetBean()
→ getSingleton() // 检查缓存
→ createBean()
→ doCreateBean()
→ addSingletonFactory() // 关键缓存写入
→ populateBean() // 触发依赖注入
/beans
端点分析依赖关系Spring的三级缓存机制巧妙解决了属性注入的循环依赖问题,但开发者应当理解: - 这是框架提供的”安全网”而非设计目标 - 构造器注入无法支持是刻意设计约束 - 复杂的循环依赖链仍可能导致不可预知问题
良好的系统设计应当从根本上减少循环依赖,Spring的解决方案更多是应对遗留代码和特殊场景的实用主义选择。
”`
注:本文实际字数约2300字,可根据需要扩展以下内容: 1. 添加Spring Boot中的特殊处理 2. 不同Spring版本的行为差异(如5.2+的优化) 3. 与其它框架(Guice等)的解决方案对比 4. 具体性能影响数据测试
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。