一个或多个对象之间存在直接或间接的依赖关系,这种依赖关系构成一个环形调用。
A和B相互调用 对标java代码样例
目前这种情况sping已经解决互相依赖的问题,后面解释原因
@Service
public class A {
@Autowired
private B b
public void testA() {
}
}
@Service
public class B {
@Autowired
private A a;
public void testB() {
}
}
相互依赖的问题原因:
在对象A创建过程中,需要注入对象B,容器中没有对象B就去创建对象B
对象B创建过程中又需要注入对象A,而对象A在等待对象B的创建,对象B在等待对象A的创建
导致两者都无法创建成功,无法加入到单例池供用户使用
总体来说,就是bean在创建过程中存在的三种对象形态集合。
一级存储集合: 完整的bean, 实例化完成,属性已赋值初始化完成
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
二级存储集合: 不完成的bean, 实例化完成,属性未赋值初始化未完成
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
三级存储集合: 存放了一个bean的lambda表达式,实例化完成但未赋值未初始化完成
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
互相依赖的问题
优先说明 (对象A == origin对象A)
1. 创建对象A的实例的时候,会先通过java反射创建original对象A(相当于new),但是还没有设置任何属性,此时将original对象A存入缓存中备用
2. 继续给对象A设置属性和依赖,发现对象A依赖对象B, 查找缓存后并未找到就去创建对象B
3. 创建对象B的过程中发现对象B依赖对象A,就从缓存中拿到original对象A进行使用,创建对象B成功后将对象B赋值给对象A,这样对象A/B都创建成功了
从上边的过程中不难发现,二级存储就解决了对象依赖的问题,那问题来了?为什么要存在三级存储呢?因为还要考虑一种特别情况--------AOP(至于AOP,后边出了相关的文档再进行关联说明),由于AOP在创建Bean后如果有需要代理的方法,那么系统就会自动配置对应的后置处理器,这样生成的对象bean就不等于二级缓存中生成的orginal对象bean了。为了解决这个问题才出现了三级缓存。
1. 创建对象A的实例的时候,会先通过java反射创建original对象A(相当于new)
2. 先去判断一级缓存中是否存在,如果不存在就向三级缓存中添加一条记录对象A-bean,key是bean的beanName,value是ObjectFactory,可以通过执行Lambda表达式可以生成对象A的代理对象
3. 再去判断二级缓存中是否存在,如果存在删除三级缓存中的数据,继续给二级缓存中的对象A-bean进行设置属性,否则使用三级缓存中的bean,设置过程中发现需要依赖对象B
4. 创建original对象B,然后去一级缓存中查询是否存在,不存在的话就在三级缓存中增加一条数据,如果二级缓存中存在就用二级缓存数据否则就用三级缓存
5. 创建originalB后设置属性时候发现对象B依赖对象A,去一级二级缓存中查询是否存在对象A,存在就使用,不存在去三级缓存中拿到对象A的bean(至于需不需要获取代理对象根据需要拿)
6. 将拿到的对象A-bean保存在对象B-bean中,这样对象依赖的对象B就创建好了,删除三级缓存数据存入二级缓存
7. 然后继续创建对象A的bean
8. 反过来创建对象B,最后对象A和对象B都生成了
并不是哦,多例、构造器注入的bean不可以,因为循环依赖的原理的实例化后提前暴露的引用,这两种情况还没实例化
依赖情况 | 依赖注入方式 | 是否支持 |
---|---|---|
AB循环依赖 | AB均采用构造器注入 | 否 |
AB循环依赖 | AB均采用setter方法注入 | 是 |
AB循环依赖 | AB均采用属性自动注入 | 是 |
AB循环依赖 | A中注入为setter注入,B中注入的A为构造器注入 | 是 |
AB循环依赖 | B中注入的A为setter注入,A中注入的B为构造器注入 | 否 |