本文讲解如何解决循环依赖,具体实现参考手写spring框架
1.为何会出现循环依赖
A依赖B,同时B又依赖A,且两者都没有提前创建好,如下图所示:
2.spring如何解决循环依赖
打破循环的原理在出现循环的时候提前将其中的一个对象创建好,并注入到另一个对象。spring中采用的三级缓存的来实现这个操作,除了三级缓存外还需要一个set来判断是否出现循环依赖,那么在spring中创建对象的流程如下:
/**
* 一级缓存,存放经过完整生命周期的单例bean对象
*/
private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
/**
* 二级缓存,存放提前进行aop代理的bean对象
*/
private ConcurrentHashMap<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
/**
* 三级缓存,存放构造器创建的原始bean对象
*/
private ConcurrentHashMap<String, Object> singletonFactories = new ConcurrentHashMap<>();
/**
* 记录正在创建中的bean,用于判断是否出现循环依赖
*/
private Set<String> creatingSet = Collections.newSetFromMap(new ConcurrentHashMap<>());
可能有眼尖的小伙伴就能看出来,二级缓存有什么用呢?spring为了保证单例的特性,会尽量保证对象在最后一步再进行Aop的动态代理操作,但是在出现循环依赖时就不得不提前进行Aop,此时就会出现两个问题:一是当A在正常流程注入B后需要进行Aop操作,此时如果A已经提前进行了Aop,就会出现B对象中的A和其他对象中的A对象不一样;二就是如果A在注入B后,还需要注入C对象,然而C对象也跟A出现循环依赖,那么此时A又需要提前Aop并且注入到C中,这样C对象中的A和B对象中的A就会出现不一致。为了避免上述情况的发送,spring会将提前Aop过的A对象放入二级缓存中,出现上述两种情况时会提前去二级缓存中查看是否有已经Aop过的A对象,保证对象的单例特性。最终流程如下: