SpringBoot三级缓存如何解决循环依赖的问题
SpringBoot三级缓存如何解决循环依赖的问题
今天简单说一下SpringBoot是如何通过三级缓存来解决循环依赖的问题。
1. 什么是循环依赖
循环依赖(Circular Dependency)是指两个或多个模块、类、组件之间相互依赖,形成一个闭环。简而言之,模块A依赖于模块B,而模块B又依赖于模块A,这会导致依赖链的循环,无法确定加载或初始化的顺序。
简单示例
@Service
public class A {@Autowiredprivate B b;
}@Service
public class B {@Autowiredprivate A a;
}//或者自己依赖自己
@Service
public class A {@Autowiredprivate A a;
}
上面这两种方式都是循环依赖,应该很好理解,当然也可以是三个Bean甚至更多Bean相互依赖,原理都是一样的。
这种循环依赖可能会产生问题,例如A要依赖B,发现B还没创建,于是开始创建B,创建的过程发现B要依赖A,而A
还没创建好呀,因为它要等B创建好,就这样它们俩就搁这卡bug了。
SpringBoot如何解决循环依赖?
简单来说就是:提前暴露,三级缓存:
- 一级缓存(Singleton Objects Map):用于存储完全初始化完成的单例Bean。
- 二级缓存(Early Singleton Objects Map):用于存储尚未完全初始化,但已实例化的Bean,用于提前暴露对象,避免循环依赖问题。
- 三级缓存(Singleton Factories Map):用于存储对象工厂,当需要时,可以通过工厂创建早期Bean(特别是为了支持AOP代理对象的创建)。
解决步骤
- Spring首先创建Bean实例,并将其加入三级缓存中(Factory)
- 当一个Bean依赖另一个未初始化的Bean时,Spring会从三级缓存中获取Bean的工厂,并生成该Bean的对象(若有代理则是代理对象)。
- 代理对象存入二级缓存,解决循环依赖。
- 一旦所有依赖Bean被完全初始化,Bean将转移到一级缓存中。
此外,为什么Spring循环依赖需要三级缓存,二级不够吗?
Spring之所以需要三级缓存而不是简单的二级缓存,主要原因在于AOP代理和Bean的早期引用问题。
二级缓存虽然可以解决循环依赖的问题,但在涉及到动态代理(AOP)时,直接使用二级缓存不做任何处理会导致我们拿到的Bean是未代理的原始对像。如果二级缓存内存放的都是代理对象,则违反了Bean的生命周期。