云计算百科
云计算领域专业知识百科平台

Spring 如何解决循环依赖?

Spring 解决循环依赖主要通过三级缓存机制来实现。

一、什么是循环依赖

循环依赖是指两个或多个 Bean 之间相互依赖,形成闭环。例如:

  • A 依赖 B
  • B 依赖 A

或者更复杂的情况:

  • A 依赖 B
  • B 依赖 C
  • C 依赖 A

二、Spring 的三级缓存机制

Spring 使用三个 Map 来管理 Bean 的实例:

// 一级缓存:存放完整的 Bean(已实例化、已填充属性、已初始化)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// 二级缓存:存放早期暴露的 Bean(已实例化、未填充属性)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

// 三级缓存:存放 Bean 工厂,用于生成早期 Bean 的代理对象
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

三、解决循环依赖的流程

以 A、B 相互依赖为例:

1. 创建 A 的实例

// 1. 实例化 A(调用构造函数)
A a = createBeanInstance("a");

// 2. 将 A 的工厂放入三级缓存
addSingletonFactory("a", () -> getEarlyBeanReference("a", a));

2. 填充 A 的属性(发现依赖 B)

// 3. 尝试获取 B
B b = getBean("b");

3. 创建 B 的实例

// 4. 实例化 B
B b = createBeanInstance("b");

// 5. 将 B 的工厂放入三级缓存
addSingletonFactory("b", () -> getEarlyBeanReference("b", b));

4. 填充 B 的属性(发现依赖 A)

// 6. 尝试获取 A
A a = getBean("a");

// 7. 从三级缓存获取 A 的工厂,创建早期引用
ObjectFactory<?> factory = singletonFactories.get("a");
A earlyA = factory.getObject();

// 8. 将早期 A 从三级缓存移到二级缓存
earlySingletonObjects.put("a", earlyA);
singletonFactories.remove("a");

5. 完成 B 的创建

// 9. B 初始化完成,放入一级缓存
addSingleton("b", b);

6. 完成 A 的创建

// 10. A 获取到 B,填充属性完成
// 11. A 初始化完成,放入一级缓存
addSingleton("a", a);

// 12. 从二级缓存移除 A
earlySingletonObjects.remove("a");

四、核心源码分析

1. getBean() 方法

protected <T> T doGetBean(String name, Class<T> requiredType, ...) {
// 1. 尝试从缓存获取
Object sharedInstance = getSingleton(beanName);

if (sharedInstance != null) {
return (T) getObjectForBeanInstance(sharedInstance, name, beanName);
}

// 2. 创建 Bean 实例
return createBean(beanName, mbd, args);
}

2. getSingleton() 方法(关键)

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 1. 从一级缓存获取
Object singletonObject = this.singletonObjects.get(beanName);

// 2. 一级缓存没有,且当前 Bean 正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 3. 从二级缓存获取
singletonObject = this.earlySingletonObjects.get(beanName);

// 4. 二级缓存没有,且允许早期引用
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// 5. 双重检查
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 6. 从三级缓存获取工厂
ObjectFactory<?> singletonFactory =
this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 7. 调用工厂方法获取早期 Bean
singletonObject = singletonFactory.getObject();
// 8. 放入二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 9. 从三级缓存移除
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}

3. createBean() 方法

protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) {
// 1. 实例化 Bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance;
}

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
// 1. 实例化
Object bean = createBeanInstance(beanName, mbd, args);

// 2. 允许循环依赖,将工厂放入三级缓存
boolean earlySingletonExposure = (mbd.isSingleton() &&
this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));

if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

// 3. 填充属性(可能触发循环依赖)
populateBean(beanName, mbd, bean);

// 4. 初始化
Object exposedObject = initializeBean(beanName, exposedObject, mbd);

// 5. 放入一级缓存
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
exposedObject = earlySingletonReference;
}
}
addSingleton(beanName, exposedObject);

return exposedObject;
}

五、为什么需要三级缓存

一级缓存不够吗?

  • 一级缓存只存放完整的 Bean,循环依赖时 Bean 还未创建完成

二级缓存不够吗?

  • 二级缓存可以解决简单循环依赖
  • 但当涉及 AOP 代理时,需要三级缓存来延迟创建代理对象

三级缓存的作用

// 三级缓存存放的是工厂,可以延迟创建代理对象
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
// 如果需要 AOP 代理,在这里创建代理对象
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp =
(SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}

六、循环依赖的限制

1. 只能解决单例 Bean 的循环依赖

// 原型 Bean 不支持循环依赖
@Scope("prototype") // 会抛出异常

2. 构造器注入无法解决

// 构造器注入会抛出 BeanCurrentlyInCreationException
@Component
public class A {
private final B b;

public A(B b) { // 构造器注入
this.b = b;
}
}

3. @Async 注解的 Bean

// @Async 会导致循环依赖失败
@Component
public class A {
@Async
public void method() {}
}

七、解决方案

1. 使用 @Lazy 注解(构造器注入)

@Component
public class A {
private final B b;

public A(@Lazy B b) { // 延迟加载
this.b = b;
}
}

2. 改用 Setter 注入

@Component
public class A {
private B b;

@Autowired
public void setB(B b) {
this.b = b;
}
}

3. 使用 @PostConstruct 初始化

@Component
public class A {
@Autowired
private B b;

@PostConstruct
public void init() {
// 在这里使用 b
}
}

4. 重新设计,避免循环依赖

  • 使用事件驱动
  • 引入中间层
  • 使用设计模式(如观察者模式)

八、总结

Spring 通过三级缓存机制巧妙地解决了单例 Bean 的循环依赖问题:

  • 一级缓存:存放完整的 Bean
  • 二级缓存:存放早期暴露的 Bean(已实例化但未填充属性)
  • 三级缓存:存放 Bean 工厂,用于生成代理对象
  • 核心思想是:提前暴露未完全初始化的 Bean 引用,让其他 Bean 可以引用它,从而打破循环依赖的僵局。

    但要注意,这种机制只适用于 Setter 注入的单例 Bean,构造器注入和原型 Bean 的循环依赖需要通过其他方式解决。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » Spring 如何解决循环依赖?
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!