Spring 解决循环依赖问题的核心在于提前暴露一个已经创建完成的对象引用,而不是提前暴露一个未初始化的代理对象。Spring 使用的三级缓存机制就是实现这个目的的。
1. 单例对象的创建过程中遇到循环依赖,Spring 采取提前暴露引用的方式解决。
2. 三级缓存:singletonObjects、earlySingletonObjects、singletonFactories
3. 解决方法:
a. 当请求Bean时,如果Bean正在创建中,则先返回一个临时的代理对象(早期代理对象),这样可以允许其他Bean引用该Bean,形成一个引用链。
b. 当Bean创建完成后,原始对象被存储在singletonObjects中,早期引用则被移到earlySingletonObjects中。
c. 当Bean属性设置完成,最终调用Bean的完成初始化方法,此时原始对象会被再次放回singletonObjects中,早期引用对象被移除。
以下是一个简化的示例,展示了Spring如何通过三级缓存解决循环依赖问题:
// 假设有两个相互依赖的Bean
public class A {
private B b;
public void setB(B b) {
this.b = b;
}
public B getB() {
return b;
}
}
public class B {
private A a;
public void setA(A a) {
this.a = a;
}
public A getA() {
return a;
}
}
// 创建Bean的过程
ObjectFactory<A> aFactory = () -> a; // 提前暴露A的引用
ObjectFactory<B> bFactory = () -> b; // 提前暴露B的引用
// 创建A
if (a == null) {
a = factoryMethod.createInstance(); // 创建A的实例
beanFactory.registerDependentBean(BEAN_NAME, BEAN_NAME);
beanFactory.registerDependentBean(BEAN_NAME, BEAN_NAME);
aFactory = () -> a;
singletonFactories.put(BEAN_NAME, aFactory);
// 提前暴露A的引用,允许B创建过程中引用A
earlySingletonObjects.put(BEAN_NAME, a);
// 设置B依赖A
a.setB(b);
}
// 创建B
if (b == null) {
b = factoryMethod.createInstance(); // 创建B的实例
beanFactory.registerDependentBean(BEAN_NAME, BEAN_NAME);
beanFactory.registerDependentBean(BEAN_NAME, BEAN_NAME);
bFactory = () -> b;
singletonFactories.put(BEAN_NAME, bFactory);
// 提前暴露B的引用,允许A创建过程中引用B
earlySingletonObjects.put(BEAN_NAME, b);
// 设置A依赖B
b.setA(a);
}
// 完成初始化
a.setB(b);
b.setA(a);
// 最后,移除早期引用对象,原始对象被存储在singletonObjects中
earlySingletonObjects.remove(BEAN_NAME);
singletonFactories.remove(BEAN_NAME);
这个过程展示了Spring如何通过提前暴露引用来解决循环依赖的问题。在创建Bean的过程中,Spring会使用三级缓存来管理对象的创建过程,保证在创建过程中依然能够提供已经初始化完成的对象引用,从而解决循环依赖的问题。