Spring是如何解决循环依赖的

在spring中我们有可能会遇到这种情况,A依赖B,B又依赖A,正常情况下,我们用@Reference或者@Autowired注解,是不会有问题的,可在我们用构造方法的时候,就会出现问题:

    public AssistantDoctorController(UserDoctorController userDoctorController) {
        this.userDoctorController = userDoctorController;
    }
    public UserDoctorController(AssistantDoctorController assistantDoctorController) {
        this.assistantDoctorController = assistantDoctorController;
    }

创建请求bean
报错的意思是被请求的bean正在被创建,是否存在循环依赖,那spring是如何判断循环依赖,又是如何解决循环依赖的呢,网络上很多文章说的有些不是很清楚,所以想写一篇博文,说的尽量清晰些。

一、为什么构造方法的循环依赖会报错

首先看下AbstractBeanFactoryBeanFactory的结构图:
AbstractBeanFactory结构图
可以发现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
  1. 在创建A的时候,A的beanName会被加入到singletonsCurrentlyInCreation中
  2. 因为A依赖了B,此时循环创建B,然后将B放入到singletonsCurrentlyInCreation中,
  3. 此时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()中有三个入参:

  1. beanName
  2. mbd:作用不大
  3. bean:此时创建的bean是通过createBeanInstance方法创建的,还没有进行populateBean方法,所以现在的an是没有任何属性注入的,用构造方法创建出来的一个Bean,而getObject返回的就是这样的一个早期的Bean,所以叫EarlyBean。
  4. 这个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的时机是在设置注入属性的时候,就是在:
  1. CommonAnnotationBeanPostProcessor设置@Resource属性时候或者
  2. 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,即使没有循环依赖也是这个处理逻辑

发布了188 篇原创文章 · 获赞 117 · 访问量 38万+

猜你喜欢

转载自blog.csdn.net/lz710117239/article/details/101988685