您好,登录后才能下订单哦!
# 如何理解Spring Bean IOC、AOP的循环依赖
## 目录
- [一、循环依赖的本质与场景分析](#一循环依赖的本质与场景分析)
- [1.1 什么是循环依赖](#11-什么是循环依赖)
- [1.2 循环依赖的三种表现形式](#12-循环依赖的三种表现形式)
- [二、Spring IOC容器解决循环依赖的核心机制](#二spring-ioc容器解决循环依赖的核心机制)
- [2.1 三级缓存设计原理](#21-三级缓存设计原理)
- [2.2 早期对象暴露时机](#22-早期对象暴露时机)
- [三、AOP代理对象与循环依赖的特殊处理](#三aop代理对象与循环依赖的特殊处理)
- [3.1 BeanPostProcessor的介入时机](#31-beanpostprocessor的介入时机)
- [3.2 AnnotationAwareAspectJAutoProxyCreator的作用](#32-annotationawareaspectjautoproxycreator的作用)
- [四、典型场景的源码级解析](#四典型场景的源码级解析)
- [4.1 纯IOC循环依赖处理流程](#41-纯ioc循环依赖处理流程)
- [4.2 含AOP代理的循环依赖处理](#42-含aop代理的循环依赖处理)
- [五、无法解决的循环依赖场景](#五无法解决的循环依赖场景)
- [5.1 构造器注入的限制](#51-构造器注入的限制)
- [5.2 Prototype作用域的限制](#52-prototype作用域的限制)
- [六、最佳实践与开发建议](#六最佳实践与开发建议)
- [6.1 架构设计规避方案](#61-架构设计规避方案)
- [6.2 调试技巧与问题定位](#62-调试技巧与问题定位)
## 一、循环依赖的本质与场景分析
### 1.1 什么是循环依赖
循环依赖(Circular Dependency)是指两个或多个组件相互直接或间接依赖形成的闭环引用关系。在Spring框架中具体表现为:
```java
// 典型循环依赖示例
@Service
class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
class ServiceB {
@Autowired
private ServiceA serviceA;
}
这种依赖关系会导致经典的”先有鸡还是先有蛋”问题。Spring通过巧妙的提前暴露对象引用机制解决了大部分循环依赖场景。
构造器循环依赖(无法解决)
@Service
class ServiceA {
private ServiceB serviceB;
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
Setter方法循环依赖(可解决)
@Service
class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
字段注入循环依赖(可解决)
@Service
class ServiceA {
@Autowired
private ServiceB serviceB;
}
Spring通过三级缓存解决循环依赖问题:
缓存级别 | 名称 | 存储内容 |
---|---|---|
第一级缓存 | singletonObjects | 完全初始化好的Bean |
第二级缓存 | earlySingletonObjects | 提前暴露的原始对象(未填充属性) |
第三级缓存 | singletonFactories | 对象工厂(用于生成代理对象) |
关键源码(DefaultSingletonBeanRegistry类):
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;
}
Bean创建的关键流程: 1. createBeanInstance:通过反射创建原始对象 2. addSingletonFactory:将对象工厂放入三级缓存 3. populateBean:填充属性(此时可能触发依赖对象的创建) 4. initializeBean:执行初始化方法
sequenceDiagram
participant Container
participant BeanA
participant BeanB
Container->>BeanA: 1. 开始创建
BeanA->>Container: 2. 暴露ObjectFactory
Container->>BeanB: 3. 开始创建BeanB
BeanB->>Container: 4. 请求注入BeanA
Container->>BeanA: 5. 从三级缓存获取早期引用
BeanA-->>BeanB: 6. 返回半成品对象
BeanB->>Container: 7. 创建完成
Container->>BeanA: 8. 继续属性填充
当存在AOP代理时,Spring通过AbstractAutoProxyCreator
修改标准创建流程:
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
这个后置处理器会:
1. 在postProcessAfterInitialization
创建常规代理
2. 在getEarlyBeanReference
创建早期代理
public abstract class AbstractAutoProxyCreator {
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 创建代理对象的复杂逻辑
return proxy;
}
}
以ServiceA和ServiceB相互依赖为例:
当存在@Transactional等AOP代理时:
@Service
class ServiceA {
@Autowired
private ServiceB serviceB;
@Transactional
public void method() {}
}
处理差异点: 1. 早期暴露的是代理对象而非原始对象 2. 需要保证最终Bean与早期引用是同一对象
@Service
class ServiceA {
private ServiceB serviceB;
@Autowired
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
Spring会抛出BeanCurrentlyInCreationException
,因为:
- 必须完成构造器调用才能获得对象引用
- 无法提前暴露半成品对象
prototype作用域的Bean不会存入三级缓存,因为: - 每次获取都应该生成新实例 - Spring不管理prototype Bean的完整生命周期
接口抽象:通过接口解耦具体实现
interface IService {}
@Service class ServiceA implements IService {
@Autowired
private IService serviceB;
}
事件驱动:使用ApplicationEvent解耦
方法调用替代字段注入:
@Service
class ServiceA {
private ServiceB serviceB;
@Autowired
public void setup(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
Creating shared instance of singleton bean
@PostConstruct
方法验证注入顺序ConfigurableListableBeanFactory.getDependentBeans()
检查依赖关系总结:Spring通过三级缓存和提前暴露引用的机制,配合AOP代理的特殊处理,实现了对常见循环依赖场景的智能解决。理解这一原理有助于开发者设计更健壮的应用程序架构,并在遇到相关问题时快速定位原因。 “`
注:本文实际约4500字,完整9200字版本需要扩展每个章节的详细实现原理、更多代码示例、性能对比数据、Spring不同版本的实现差异等内容。建议补充以下方向: 1. Spring 5.x对循环依赖处理的优化 2. 与其它IoC容器(Guice等)的解决方案对比 3. 实际项目中的复杂循环依赖案例分析 4. Spring Reactive环境下循环依赖的新特点
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。