Spring是如何解决循环依赖的
在spring中我们有可能会遇到这种情况,A依赖B,B又依赖A,正常情况下,我们用@Reference或者@Autowired注解,是不会有问题的,可在我们用构造方法的时候,就会出现问题:
public AssistantDoctorController(UserDoctorController userDoctorController) {
this.userDoctorController = userDoctorController;
}
public UserDoctorController(AssistantDoctorController assistantDoctorController) {
this.assistantDoctorController = assistantDoctorController;
}
报错的意思是被请求的bean正在被创建,是否存在循环依赖,那spring是如何判断循环依赖,又是如何解决循环依赖的呢,网络上很多文章说的有些不是很清楚,所以想写一篇博文,说的尽量清晰些。
一、为什么构造方法的循环依赖会报错
首先看下AbstractBeanFactoryBeanFactory的结构图:
可以发现AbstrapctBeanFactory继承了DefaultSingletonBeanRegistry类,这个类是注册bean使用的,可见注册bean也是在AbstractBeanFactory中完成,在注册bean之前会调用它的beforeSingletonCreation方法,如下:
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
这个方法中会检查singletonsCurrentlyInCreation属性是否存在这个bean,没有就放入,有则会抛出异常,等到创建bean完成,再调用afterSingletonCreation方法将已经创建的bean删除。
- singletonsCurrentlyInCreation:记录正在创建中的bean。
我举例说明:
A->B->A
- 在创建A的时候,A的beanName会被加入到singletonsCurrentlyInCreation中
- 因为A依赖了B,此时循环创建B,然后将B放入到singletonsCurrentlyInCreation中,
- 此时B又依赖了A,然后再将A放入到singletonsCurrentlyInCreation中,此时因为有A在singletonsCurrentlyInCreation中,所以放入失败,就会抛出异常。
为什么用注解循环依赖不会报错
因为在创建bean的时候,会调用bean的构造方法去实例化bean,并缓存到beanFactory中,而用注解做的构造方法中,不需要在构造方法中注入bean,所以不会报错。
总结就是:构造方法中的bean是在创建bean的时候注入的,而注解的bean是后面注入的,所以报错是因为构造方法的缘故。
那么问题又来了,注解注入的bean是在什么时间注入的呢?
注解注入Bean的时机
真正的执行注入,是在当前bean的依赖bean创建完成之后的,AbstractAutowireCapableBeanFactory类中的doCreateBean方法中的populateBean方法中完成的。
代码如下:
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
其中注入@Resource注解,使用的是CommonAnnotationBeanPostProcessor,注入@AutoWired注解使用的是AutowiredAnnotationBeanPostProcessor,执行顺序是限制性CommonAnnotationBeanPostProcessor,再执行AutowiredAnnotationBeanPostProcessor,所以注入的时候会先根据名称注入,然后根据类型进行注入。
循环依赖的解决
那么注入了Bean之前,如果有循环依赖如何解决呢,我们要先明白几个属性,几个DefaultSingletonBeanRegistry的属性,而我认为理解循环依赖的关键就是弄懂这几个属性,以及他们何时进行了操作
- singletonFactories:bean的创建方法的缓存。
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
这个值是什么时候设置进去的呢?是在doCreateBean方法中,创建了BeanInstance之后,代码如下:
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
这个ObjectFactory的getObject()中有三个入参:
- beanName
- mbd:作用不大
- bean:此时创建的bean是通过createBeanInstance方法创建的,还没有进行populateBean方法,所以现在的an是没有任何属性注入的,用构造方法创建出来的一个Bean,而getObject返回的就是这样的一个早期的Bean,所以叫EarlyBean。
- 这个beanName对应的ObjecFactory会在EarlyBean添加到earlySingletonObjects属性中后进行删除,代码如下:
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
- registeredSingletons:DefaultSingletonBeanRegistry的这个属性是按照依赖的顺序放入了beanName,在spring的使用中无用,主要是给springMoce或者其他方面,可以不用关注。
- earlySingletonObjects:这个属性的key为beanName,value是我们上面说的earlyBean。bean放入到earlySingletonObjects的时机是在设置注入属性的时候,就是在:
- CommonAnnotationBeanPostProcessor设置@Resource属性时候或者
- AutowiredAnnotationBeanPostProcessor设置@Autowired属性的时候
通过beanName调用DefaultSingletonBeanRegistry的getSingleton方法的时候进行的插入,以及singletonFactories中属性的删除
- singletonObjects:这个属性中缓存了beanName和对应的已经注入好的Bean
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
这个里面的Bean会在Bean中的注入属性已经完成注入,成为一个完整正常的bean之后放入的。它是在AbstractBeanFactory的doGetBean方法中的DefaultSingletonBeanRegistry的addSingleton方法中完成的。
也有人把Bean的循环依赖注入成为三级缓存完成那么
- 第一层缓存:singletonFactories
- 第二层缓存:earlySingletonObjects
- 第三层缓存:singletonObjects
总结:这就是Bean的解决循环依赖的方法与过程,正常的Bean,即使没有循环依赖也是这个处理逻辑