预备知识
循环依赖开关(方法) - AbstractAutowireCapableBeanFactory#setAllowCircularReferences
- 单例工程(属性) - DefaultSingletonBeanRegistry#singletonFactories
- 获取早期未处理Bean (方法) - AbstractAutowireCapableBeanFactory#getEarlyBeanReference
- 早期未处理Bean (属性) - DefaultSingletonBeanRegistry#earlySingletonObjects
循环依赖处理流程
案例代码:
@Component
public class ClassRoom {
@Autowired
private Collection<Student> students;
public Collection<Student> getStudents() {
return students;
}
public void setStudents(Collection<Student> students) {
this.students = students;
}
}
@Component
public class Student {
@Autowired
private ClassRoom classRoom;
public ClassRoom getClassRoom() {
return classRoom;
}
public void setClassRoom(ClassRoom classRoom) {
this.classRoom = classRoom;
}
}
这个例子ClassRoom里面注入了Student,Student里面又注入了ClassRoom。
首先要确保我们的循环依赖是单例的,然后再初始化单例Bean的时候会调用doGetBean方法,然后进入AbstractBeanFactory#doGetBean方法:
在getSington方法里面beforeSingletonCreation,有这样一个方法,这个方法会将当前正在创建的单例Bean放到singletonsCurrentlyInCreation中:
接着会调用我们拉姆达表达式穿进来的工厂createBean获取Bean,会判断是不是正在创建的Bean
如果是的话(前面在beforeSingletonCreation将当前创建的Bean放到了singletonsCurrentlyInCreation所以这里肯定是),会调用addSingletonFactory,会传进去一个拉姆达表达式:
这个时候singletonFactories就会存贮,并且earlySingletonObjects如果存在会移除这个Bean,同时将这个Bean放到已注册中:
接着就是这个Bean的populateBean逻辑,对依赖进行注入会处理依赖doResolveDependency:
由于我们注入Students是一个集合,所以会走multipleBeans这个处理:
这个时候又去getBean了,这个和我们上面ClassRoom走的是一样的流程,这个时候已经有两个了:
在Student注入的时候发现注入的字段又需要ClassRoom,于是又会去resolveDependency:
这个时候earlySingletonObjects里面没有,所以拿到了我们上面的singletonFactories中的singleFactory了,在拿到Bean以后就将singletonFactories中的移除了,同时将它放到earlySingletonObjects:
然后将这个ClassRoom的Bean注入到Student中:
我们可以看到这个时候ClassRoom里面是没有注入Students的:
接下来就是调用了addSington,里面如果判断是新创建的Sington做了这样几件事情:1)移除将当前Bean放到singtonObjects中 2)将singtonFactories中的singleFactory移除
然后回到了ClassRoom,这个时候Student的Bean已经进行了创建,进行注入即可。同时将这个Bean从创建中进行移除。
这就解决了循环依赖,我的理解是通过Map存储早期的Bean,然后注入的也是早期的Bean,等到它依赖的Bean完成注入的时候,注入的早期的Bean的引用也会发生变化,类似于一个延迟加载。