文章字数:770,阅读全文大约需要3分钟
一、什么是循环依赖
循环依赖就是两个或者以上的bean
相互持有对方,形成了一个依赖闭环。
循环依赖的场景有
- 构造器的循环依赖
field
属性的循环依赖
二、检测循环依赖
Spring
创建Bean
时是递归创建的,即发现属性有依赖的Bean
就回去创建属性对象的Bean
。- 检测循环依赖就是在创建
Bean
的时候做个标记,当再次递归调用创建的时候,发现有创建的标记就知道了
三、解决循环依赖
三级缓存
1
2
3
4
5
6
7
8/** 存放单例bean 1级*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** 存放初始化,但还没赋值的bean的工厂(存放函数接口、钩子函数等) 3级*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/** 存放提前曝光的bean 2级*/
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);bean实例化过程
- createBeanInstance实例化bean
- populateBean填充属性
- initializeBean初始化bean
依赖循环发送在实例化bean(调用构造方法)和填充属性(递归创建bean)的时候
- 创建流程
- 执行构造函数,创建一个未初始化的类
- 将初始化的类暴露到
singletonFactories
中,并标记已创建(加入registeredSingletons
) - 填充属性,属性中包含其他
bean
则递归进入创建流程 - 递归创建的属性假如也依赖最早的类,则会先尝试获取它
- 尝试在一级缓存(
singletonObjects
)获取 - 当获取不到,且改
bean
被标记过正在创建尝试在二级缓存(earlySingletonObjects
)中获取提前暴露的类 - 找不到则从三级缓存(
singletonFactories
)获取。 - 此时就找到了创建但未初始化的类
- 找到后将三级缓存中的类工厂移除,将类添加到提前曝光的二级缓存中
earlySingletonObjects
- 然后返回本身,此时最初的类获取到属性的
bean
后也能创建成功了。 四、三级缓存的意义
- 二级缓存也可以解决循环依赖,一个存正常的单例,一个存提前曝光的类
- 三级缓存主要是在曝光且提前引用时能够做一下拓展操作,二级缓存只能知道提前曝光,三级缓存转移到二级缓存才是曝光并提前引用
- 每个对象创建之后会放在三级缓存中,暴露自己的构造工厂
- 然后执行初始化方法,填充需要的依赖。
- 递归创建依赖对象时,如果发现需要注入的对象是之前递归过程中创建并初始化的(二级缓存中的),或者是创建未初始化的(三级缓存中),就从缓存中拿出来
- 如果是从三级缓存拿的,从三级缓存中移除,放到二级缓存中
关键词: java
, spring