Spring到底是如何解决循环依赖的?

Posted 非让修改账号名称

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring到底是如何解决循环依赖的?相关的知识,希望对你有一定的参考价值。

Q:spring如何解决循环依赖的问题?

A:三级缓存。


spring中的循环依赖有三种情况,构造器之间的循环依赖,构造器和setter之间的循环依赖,setter/field之间的循环依赖,spring只能解决setter/field之间的循环依赖三级缓存指的是singletonObjects、earlySingletonObjects和singletonFactories,singletonObjects用于存储完整的单例对象,earlySingletonObjects和singletonFactories用来存储提前曝光的、没有完成所有初始化流程的单例对象,网上的很多博客都没有把”提前曝光“这个东西说清楚,以至于搞不清楚为啥要整三级缓存,二级缓存不是也能解决问题么。


循环依赖发生在创建bean对象的过程中,也就是调用BeanFactory的getBean()方法的时候,getBean()的整体逻辑可以分为两条线来看,一条是doGetBean(),一条是createBean(),虽然这两条线都属于getBean(),但是spring非常巧妙的用第三极缓存和一个匿名内部类进行了解耦,高手就是高手,我等菜鸡只能高手指。


先看看代码逻辑,有点多,可以在idea中对照spring的源码跟一下。

第一条线:getBean()protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {    /**      这里,一进来直接尝试从缓存中获取,如果从一级缓存中拿到了,      就返回,否则从二级缓存中那,如果没拿到,再尝试从三级缓存中      拿,如果拿到了,就把它从三级缓存提升到二级缓存,      这个方法是解决循环依赖的第一个关键逻辑。①    */ Object sharedInstance = this.getSingleton(beanName);if (mbd.isSingleton()) {        /**           这里的getSingleton方法封装了doGetBean()这第一条线的           整体逻辑,接下面的方法。②        */ sharedInstance = this.getSingleton(beanName, () -> {try {return this.createBean(beanName, mbd, args); } catch (BeansException var5) {this.destroySingleton(beanName);throw var5; } }); bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } }//①public Object getSingleton(String beanName) {return this.getSingleton(beanName, true);}//①protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) { synchronized(this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {          /**            这里就是向三级缓存中          */ ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);if (singletonFactory != null) { singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName); } } } }//② public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { ...其他造成//singletionFactory上一层调用传过来的匿名内部类/** 这里调用doCreateBean(beanName)方法,在这个方法中,为这个bean向三级缓存中添加了一个 ObjectFactory,然后进行属性赋值和初始化操作 */ singletonObject = singletonFactory.getObject(); ..其他操作//添加到一级缓存中 this.addSingleton(beanName, singletonObject);return singletonObject; }} 第二条线:createBean()protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { instanceWrapper = this.createBeanInstance(beanName, mbd, args); ....    /**      将实例对象曝光到三级缓存singletonFactories中,      这里是用一个ObjectFactory对象保存了通过反射创建的实例对象      这里是解决循环依赖的第二个关键点 ③    **/this.addSingletonFactory(beanName, () -> {        //调用这个匿名内部类时,在返回原对象的时候        //扫描了IoC容器中所有的SmartInstantiationAwareBeanPostProcessor        //并指定了对应的callback方法getEarlyBeanReference        //这是spring的一个扩展点return this.getEarlyBeanReference(beanName, mbd, bean); });}//③protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized(this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {            //将上面的Objectfactory类型的匿名内部类放到一级缓存this.singletonFactories.put(beanName, singletonFactory);          this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName); } }}protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean;if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) { Iterator var5 = this.getBeanPostProcessors().iterator();while(var5.hasNext()) { BeanPostProcessor bp = (BeanPostProcessor)var5.next();if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor)bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } }return exposedObject;}

下面用一个例子来说明一下三级缓存解决循环依赖的过程,假设有两个类A和B,通过setter/field进行循环依赖,当试图通过getBean(A)方法获取A的实例对象时,首先尝试从缓存中拿,此时三个缓存中都没有,然后就会调用第一个匿名ObjectFactory的getObject方法,触发对createBean(beanName)的调用,在createBean中首先通过反射获取到类的实例对象,然后将其封装到一个ObjectFactory中并放入到三级缓存,然后按照Bean的初始化流程进行属性赋值(@Autowired),此时发现其依赖于B对象,然后再调用getBean(B)尝试获取B对象,B对象在执行到属性赋值的时候发现依赖于A,再调用getBean(A),关键的地方来了,此时由于A已经在三级缓存singletonFactories中了,所以就会从三级缓存中拿到那个匿名的ObjectFactory,然后调用getObject()得到没有初始化完成的A对象,然后第二个匿名的ObjectFactory触发三级升二级,此时A就在二级缓存中了,紧接着B完成整个初始化流程,最终被放到一级缓存singletonObjects中,A的初始化过程从getBean(B)中返回,继续执行,完成初始化和生命周期hook的调用,最终被放到一级缓存singletonObjects中,循环依赖的问题的解决了。


spring解决循环依赖其实就是利用了java bean对象实例化的时候可先不对属性进行赋值的特点,利用三级缓存把循环依赖中断,使其中一个对象完成初始化,再去初始化另外一个。


二级缓存和三级缓存从功能上没啥区别,只不过期间要调用spring的一个扩展点接口的方法,如果没有这一步,我觉得完全可以把这两个缓存合并成一个。

以上是关于Spring到底是如何解决循环依赖的?的主要内容,如果未能解决你的问题,请参考以下文章

Java面试小短文Spring 如何解决循环依赖?

Java面试小短文Spring 如何解决循环依赖?

Java面试小短文Spring 如何解决循环依赖?

Java面试小短文Spring 如何解决循环依赖?

深谈Spring如何解决Bean的循环依赖

Spring循环依赖原因及如何解决