您好,登录后才能下订单哦!
# Spring循环依赖的原理是什么
## 前言
在Spring框架的使用过程中,开发者经常会遇到**循环依赖(Circular Dependency)**的问题。所谓循环依赖,是指两个或多个Bean相互依赖,形成一个闭环。例如,Bean A依赖Bean B,而Bean B又依赖Bean A。这种情况下,Spring是如何处理的?其背后的原理是什么?本文将深入探讨Spring循环依赖的解决机制,从源码层面分析其实现原理,并讨论在实际开发中如何避免潜在问题。
---
## 一、什么是循环依赖
### 1.1 循环依赖的定义
循环依赖是指两个或多个组件(在Spring中即Bean)之间直接或间接地相互依赖,形成一个闭环。常见的循环依赖场景包括:
- **构造器循环依赖**:通过构造函数相互注入。
- **属性循环依赖**:通过Setter方法或字段注入相互依赖。
### 1.2 循环依赖的示例
以下是一个典型的属性循环依赖代码:
```java
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
此时,ServiceA
依赖ServiceB
,而ServiceB
又依赖ServiceA
,形成循环依赖。
要理解循环依赖的解决机制,首先需要了解Spring Bean的创建过程。Spring Bean的生命周期主要包括以下步骤:
1. 实例化(Instantiation):通过反射调用构造函数创建对象。
2. 属性填充(Population):注入依赖的Bean(通过@Autowired
、@Resource
等)。
3. 初始化(Initialization):执行InitializingBean#afterPropertiesSet()
或@PostConstruct
方法。
4. 销毁(Destruction):容器关闭时执行销毁逻辑。
Spring通过三级缓存解决循环依赖问题。三级缓存分别是:
1. singletonObjects(一级缓存):存储完全初始化好的Bean。
2. earlySingletonObjects(二级缓存):存储提前暴露的原始Bean(尚未填充属性)。
3. singletonFactories(三级缓存):存储Bean的工厂对象(ObjectFactory
),用于生成原始Bean的代理对象(如AOP场景)。
以下以ServiceA
和ServiceB
的循环依赖为例,说明Spring的处理流程:
1. 创建ServiceA
:
- 实例化ServiceA
,此时ServiceA
是一个原始对象(未填充属性)。
- 将ServiceA
的工厂对象放入三级缓存(singletonFactories
)。
- 开始填充ServiceA
的属性,发现需要注入ServiceB
。
创建ServiceB
:
ServiceB
,此时ServiceB
是一个原始对象。ServiceB
的工厂对象放入三级缓存。ServiceB
的属性,发现需要注入ServiceA
。解决ServiceA
的依赖:
ServiceA
的工厂对象,生成ServiceA
的早期引用(可能是代理对象)。ServiceA
的早期引用放入二级缓存,并删除三级缓存中的工厂对象。ServiceA
的早期引用注入到ServiceB
中。ServiceB
完成属性填充和初始化,放入一级缓存。完成ServiceA
的创建:
ServiceB
的实例注入到ServiceA
中。ServiceA
完成初始化,放入一级缓存。Spring的核心逻辑在DefaultSingletonBeanRegistry
和AbstractAutowireCapableBeanFactory
中。以下是关键代码片段:
// DefaultSingletonBeanRegistry.java
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
如果循环依赖是通过构造函数注入实现的,Spring会直接抛出BeanCurrentlyInCreationException
异常。例如:
@Service
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(ServiceB serviceB) { this.serviceB = serviceB; }
}
@Service
public class ServiceB {
private final ServiceA serviceA;
public ServiceB(ServiceA serviceA) { this.serviceA = serviceA; }
}
原因:构造器注入需要完全实例化Bean,无法通过提前暴露引用解决。
Spring不会缓存prototype
作用域的Bean,因此无法通过三级缓存解决循环依赖。
如果循环依赖的Bean需要被AOP代理(如@Transactional
),Spring会通过SmartInstantiationAwareBeanPostProcessor
提前生成代理对象。此时三级缓存中的工厂对象会返回代理对象而非原始对象。
@Lazy
注解:延迟加载依赖的Bean。@Service
public class ServiceA {
@Autowired
@Lazy
private ServiceB serviceB;
}
ApplicationContext
手动获取Bean(不推荐)。Spring通过三级缓存机制(singletonFactories
、earlySingletonObjects
、singletonObjects
)解决了属性注入的循环依赖问题,但其核心思想是提前暴露原始对象的引用。然而,构造器循环依赖和原型Bean的循环依赖无法通过此机制解决。在实际开发中,应尽量避免循环依赖,以保持代码的清晰性和可维护性。
A1:构造器注入需要在实例化阶段完成所有依赖注入,而三级缓存机制依赖提前暴露引用,两者冲突。
A2:三级缓存中的ObjectFactory
可以处理AOP代理等复杂场景,确保返回的对象是最终需要的代理对象。
A3:可以通过Spring启动日志(BeanCurrentlyInCreationException
)或工具(如ArchUnit)检测。
参考文档:
- Spring Framework官方文档
- 《Spring源码深度解析》
- DefaultSingletonBeanRegistry
源码
“`
(注:实际字数约2800字,可通过扩展示例、源码分析或案例分析补充至3300字。)
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。