Spring循环依赖指的是在Spring应用程序中,两个或多个Bean之间相互依赖,导致一个Bean的创建依赖于另一个Bean的创建,而后者又依赖于前者,从而形成一个循环依赖。通常,这种依赖关系会导致Bean的创建失败。
例如,假设有两个类 A
和 B
,它们彼此依赖:
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
public class B {
private A a;
public B(A a) {
this.a = a;
}
}
在这种情况下,Spring无法初始化这两个Bean,因为A依赖于B的实例化,而B依赖于A的实例化,形成了一个循环依赖。
解决方法
在Spring框架中,三级缓存(three-level cache)是一种用于解决单例Bean循环依赖问题的机制。具体来说,三级缓存分为以下三个层次:
- 一级缓存(Singleton Objects):用于存储完全初始化的单例Bean。
- 二级缓存(Early Singleton Objects):用于存储部分初始化的Bean,主要是那些已经实例化但还没有完成所有依赖注入的Bean。
- 三级缓存(Singleton Factories):用于存储对象工厂,这些工厂可以在需要时创建Bean实例的代理对象,通常是为了解决循环依赖问题。
步骤详解
-
创建A的实例:
- Spring首先尝试创建Bean
A
的实例。此时,A
的构造方法会被调用,但依赖的B
还没有被注入。
- Spring首先尝试创建Bean
-
将A的实例放入三级缓存:
- Spring将
A
的实例放入三级缓存中。三级缓存保存的是一个工厂,可以生成A
的代理对象或是完成依赖注入的A
实例。
- Spring将
-
创建B的实例:
- 在创建
B
的实例时,Spring发现B
依赖于A
。此时A
还没有完全初始化,但已经有了一个A
的实例在三级缓存中。
- 在创建
-
从三级缓存获取A的实例:
- Spring通过三级缓存中的工厂获取
A
的实例,完成对B
的依赖注入。此时,A
的实例可能是一个未完全初始化的代理对象,但可以暂时满足B
的依赖。
- Spring通过三级缓存中的工厂获取
-
完成B的初始化:
- 完成
B
的所有依赖注入和初始化,将B
的实例放入一级缓存。
- 完成
-
完成A的初始化:
- 回到
A
的初始化过程,现在B
已经完全初始化,可以将B
注入到A
中。然后将A
从三级缓存移到一级缓存。
- 回到
三级缓存的具体作用
- 一级缓存:存储完全初始化的Bean,避免重复初始化。
- 二级缓存:存储部分初始化的Bean,防止多次实例化同一个Bean(但不解决循环依赖)。
- 三级缓存:通过工厂模式,允许在初始化过程中获取未完全初始化的Bean,打破循环依赖。
代码示例(简化版)
class SingletonFactory {
private final ObjectFactory<?> factory;
public SingletonFactory(ObjectFactory<?> factory) {
this.factory = factory;
}
public Object getObject() {
return this.factory.getObject();
}
}
public class CustomBeanFactory {
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
private final Map<String, SingletonFactory> singletonFactories = new ConcurrentHashMap<>();
public Object getBean(String beanName) {
Object singleton = this.singletonObjects.get(beanName);
if (singleton == null) {
singleton = this.earlySingletonObjects.get(beanName);
if (singleton == null) {
SingletonFactory factory = this.singletonFactories.get(beanName);
if (factory != null) {
singleton = factory.getObject();
this.earlySingletonObjects.put(beanName, singleton);
this.singletonFactories.remove(beanName);
}
}
}
return singleton;
}
public void registerBean(String beanName, ObjectFactory<?> factory) {
this.singletonFactories.put(beanName, new SingletonFactory(factory));
}
public void addSingleton(String beanName, Object singleton) {
this.singletonObjects.put(beanName, singleton);
this.earlySingletonObjects.remove(beanName);
this.singletonFactories.remove(beanName);
}
}
通过这种机制,Spring能够有效地解决Bean的循环依赖问题,确保所有Bean都能正确地初始化和注入。