spring循环依赖解决过程&Bean的生命周期

目前两个Bean,一个TesA,一个testB,
TestA中有属性testB,TestB中有属性testA,并且相互注入,相互依赖。
spring能帮助我们解决属性上的相互依赖,而不能解决构造器的依赖。

三级缓存,都是map:

    第一级缓存, singletonObjects
    
    第二级缓存,earlySingletonObjects

    第三级缓存,singletonFactories (存放的是ObjectFactory<T>)


为了测试方便,我们将使用原生的xml配置文件,使用ClassPathXmlApplicationContext测试循环依赖的过程。

首先
getBean(testA)  

-> doGetBean(testA) 

        ->首先来到 getSingleton(testA) -> 首先从一级缓存中singletonObjects查找,显然刚开始没有这个bean ,并且这个bean也没有在创建当中,首先第一次进入这个方法,返回null。

        ->然后来到 getSingleton(beanName,singletonFactory)->首先从一级缓存中找,显然没有。-> 调用了beforeSingletonCreation(beanName),目的是在创建这个bean之前,

          先把这个beanName放到singletonsCurrentlyInCreation中,告诉容器,这个bean正在被创建。->singletonFactory.getObject();调用传过来的singtonFactory的getObject方法,
 
          注意这里的singtonFactory是doGetBean方法的匿名内部类对象,并且里面原本就持有了beanName,mbd,和对所在类的createBean方法的引用,相当于具备了产生bean的原材料。

        ->所以调用这个匿名内部类对象的getObject方法,也就开始了createBean的逻辑,即开始了创建Bean的逻辑,

                首先:

                     先经过InstantiationAwareBeanPostProcessor后置处理器的postProcessBeforeInstantiation(beanClass,beanName)方法,

                     如果上面这个方法有返回值,那么就继续调用所有普通后置处理器的applyBeanPostProcessorsAfterInitialization方法,然后这个bean就已经被创建好了。

                     也就是说spring在下一步按照spring自己的逻辑创建bean前,给了我们这样一个机会去覆盖掉后面的创建bean的逻辑,我们可以配置这样的后置处理器,重写这个方法就行。

                     如果上面这个方法没有返回值,那么来到doCreateBean,来开始创建bean了。

        ->doCreateBean,

                     调用createBeanInstance创建出instanceWrapper,一个包装了bean的对象。这个时候bean已经被创建出来了,但还未设置属性,即未初始化。

                     好,这个时候又经过了MergedBeanDefinitionPostProcessor后置处理器的postProcessMergedBeanDefinition(beanDefinition, beanType, beanName)方法,

                     也就是在bean刚刚被实例化出来的时候,spring容器给了我们一个修改beanDefintion的机会,因为下面spring将会用到beanDefiniton中的信息做一些判断和设置,这也是我们修改的好机会


        -> 然后判断是否允许循环依赖,当前要创建的beanName是不是在singletonsCurrentlyInCreation,

             很显然是,因为刚刚在创建前就加入进去的嘛。也就是说spring在刚刚实例化bean但还未初始化时,默认把正在创建的bean准备加入到三级缓存中。

       -> 上一级的条件满足了,所以到了addSingletonFactory(beanName,singletonFactory )方法, 第二个参数是,(ObjectFactory)()->getEarlyBeanReference(beanName, mbd, bean)

            注意这里的singletonFactory是doCreateBean方法的匿名内部类对象,并且它里面持有了beanName,mbd,和未初始化的bean,和对所在类的getEarlyBeanReference的方法的引用。

            在addSingletonFactory方法中,判断如果当前一级缓存中如果没有这个beanName的话,显然没有,那就把beanName和这个singletonFactory放到singletonFactories三级缓存中,

            注意这个singletonFactory前面说过了它是个匿名内部类对象哦。然后从二级缓存中移除,这里移除可有可无,因为这个时候二级缓存里面现在根本就没有东西。

          【这个时候的状态是: 刚刚实例化了testA,然后把包含testA的一个匿名内部类对象放到了三级缓存里面,这个匿名内部类对象还持有对所在类的getEealyReference方法的引用】

           那么其实按照一般情况来说,每次去doCreateBean的时候,都会创建好bean,并且在不修改默认配置的情况下,一般都会把这个未初始化的单例bean及mbd,beanName以匿名对象的方式,

           放到三级缓存中去,也就是说spring不管你要不要解决循环引用,反正这个刚创建的单例bean,这个三级缓存我是放定了,之后会不会被使用到,我spring是不知道的。

       ->现在就要开始准备给刚刚实例化的testA初始化,开始了populate(beanName,mbd,bw)的逻辑,

                   首先:

                         经过InstantiationAwareBeanPostProcessor后置处理器的postProcessAfterInstantiation( bean, beanName)方法,判断是否要对当前bean进行属性设置,

                         如果返回false,就不会给当前bean进行一个属性赋值了,这个populate方法到这里就结束了。

                         这里也就是说spring刚实例化完一个bean,又给了我们这样的一个机会让我们决定是否要进行spring的属性设置逻辑,并且我们可以使用这个方法, 拿到bean和beanName走自己想要的逻辑,

                         然后决定是不是还要走spring的的属性设置逻辑。 一般我们会让spring继续走它的属性设置逻辑,继续往下。

                  然后:

                        从mbd中取出PropertyValues属性,即pvs。这里一般是我们在xml文件中配置的property读取进去的,还可以注意点哦,前面在刚刚实例化完bean后,给了我们一个修改mbd的机会呢

                        从mbd中取出autowireMode,即获取当前bean的一个注入模式,spring提供了2种,一种是byName,一种是byType,这两种都是全注入哦。

                                     以byName为例,调用autowireByName(beanName, mbd, bw, newPvs);传入了4个参数,newpvs就是从mbd拿到的propertyValues,即pvs,

                                                                遍历bw的所有属性,如果能够被写入,并且这个属性没有在AbstractAutowireCapableBeanFactory的ignoredDependencyInterfaces忽略的依赖接口中,

                                                                并且也不是简单的属性,那就把这样的属性给收集起来,然后对于每个属性名,先调用containsBean判断有没有,

                                                                有的话,立即调用getBean,获取到这个属性对应的名字和bean然后添加到newPvs中,如果contansBean判断没有的话就算了。

                                                        伊,这里看到了个igonoredDependencyInterfaces这个东西,也就是如果被加入这个set里面的所有接口中的每一个方法,如果被哪个bean实现了,那么这个bean的这个

                                                              设置属性的方法,将不在此处被添加进pvs进行属性赋值。因为spring容器中会对实现了这些接口的bean在指定的地方会被回调,而不需要在此处处理。

                                     byType的逻辑比较复杂,但整体上估计思路和byName是一致的,也是getBean,不过是根据type来找了,然后设置给newPvs。

                                     那么注意,此时,无论是byName或byType现在都是没有给bean进行属性赋值的。而仅仅是放到了newPvs中

                        判断是否有InstantiationAwareBeanPostProcessor后置处理器 ,回调这个后置处理器的postProcessPropertyValues( pvs,  pds, bean, beanName)方法,

                                     这里有必要留意一下这个pds,pds是从bw中排除掉哪些忽略依赖接口的属性。然后接下来的实现就看各种后置处理器是怎么玩的了,

                                    大名鼎鼎的AutowiredAnnotationBeanPostProcessor就是在此处通过反射拿到bean对应的class的属性和方法上的自动注入注解,完成对bean的属性设置操作

                        然后,获取mbd的DependencyCheck,判断是否需要依赖检查,如果不是0的话,就需要进行一个依赖检查。如果这个pds数组还存在对象,并且这个对象匹配上了依赖检查的3种情况中的1种,

                                    那么就不玩了,直接抛异常,所以好自为之。

            最后

                    上面的后置处理器处理完了,最终调用applyPropertyValues(beanName, mbd, bw, pvs)将这些pvs应用到bean。

                    首先new一个BeanDefinitionValueResolver,传入了当前beanFactory等。逐个解析pvs,最终还是回到了调用beanFactory的getBean方法,只不过这个时候,开始要获取testB了

       ->getBean(testB)

       ->doGetBean(testB)

            ->getSingleton(beanName),先从一级缓存中去拿,很显然没有,并且这个Bean也没有在创建中,现在是第二次进入这个方法了哦,还是返回null,这个时候应当注意testA正在

               处于创建当中,而testB没有处在创建当中,

            ->然后来到 getSingleton(beanName,singletonFactory),这里的singletonFactory是个匿名对象包含了对原createBean方法的引用->首先从一级缓存中找,显然没有。

               调用了beforeSingletonCreation(beanName),目的是在创建这个bean之前,先把这个beanName放到singletonsCurrentlyInCreation中,告诉容器,这个bean也正在被创建。

               singletonFactory.getObject();调用传过来的singtonFactory的getObject方法,注意这里的singtonFactory刚刚也说过doGetBean方法的匿名内部类对象,

               并且里面原本就持有了beanName,mbd,和对所在类的createBean方法的引用,相当于具备了产生bean的原材料。

       ->createBean,同样的继续走testB的createBean的流程,同样先经过InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation方法,如果有返回,就走所有普通

         后置处理器的postProcessAfterInitialization方法。没有返回的话走doCreateBean

       ->doCreateBean,继续createBeanInstance创建出testB这个实例,但未初始化。应用MergedBeanDefinitionPostProcessor后置处理器的postProcessMergedBeanDefinition方法,

         这里判断是否允许循环应用并且当前的beanName是否正在被创建,显然之前吧testB添加进去了。

       ->所以就走addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))这个方法,在这个方法里面从一级缓存里面判断是否存在当前的beanName,显然

         不存在,就把当前的beanName和singletonFactory放到了singletonFactories三级缓存中,并且从二级缓存中把当前beanName移除掉,可现在二级缓存根本就没东西,无所谓。

        【这个时候的状态是: 把包含testA和testB的匿名内部类对象都分别放到了三级缓存里面,这两个匿名内部类对象还持有对所在类的getEealyReference方法的引用,并且还持有原 beanName、mbd、bean】


       ->现在开始给testB进行一个初始化,跟前面testA的初始化一样,在testB里面找到了testA的属性,然后去getBean(testA),然后doGetBean(testA),好,又来到了getSingleton(beanName)这里,

       ->getSingleton(beanName),先从一级缓存中找,显然没有。接着判断testA是否正在创建当中,很显然是!(相当于在这里终于检测出来了循环依赖:想要获取的bean还在当中),又先从二级缓

           存中拿,二级缓存这个时候是空的,拿不到。现在从三级缓存中拿testA,显然能够拿到,之前放进去过。拿到的是ObjectFactory类型的那个匿名对象singletonFactory。接着 调用这个匿名对象

          的getObject方法,这就有意思了,原来存的() -> getEarlyBeanReference(beanName, mbd, bean)里的这个getEarlyBeanReference方法被调用,方法是在 AbstractAutowireCapableBeanFactory中,

          在这个类的这个方法中,原先的beanName、mbd、bean都还在作为方法的参数,在这个方法里面, 回调SmartInstantiationAwareBeanPostProcessor后置处理器的getEarlyBeanReference方法,

          如果这个方法有返回值的话,那么将替换掉bean作为exposedObject返回,这个方法通常被用作做代理的实现。因为现在还没有这样的后置处理器,所以直接就返回了之前实例化的testA,

          注意这里的testA是直接从三级缓存中的通过代理处理过返回的。再把beanName和testA放到earlySingletonObjects二级缓存里面去,并且从三级缓存中移除掉testA。

          所以这里就相当于把三级缓存中的对象经过一个代理处理后,放入二级缓存,至于 代理与否,要看具体情况了。

        【这个时候的状态: 三级缓存中只有一个testB相关的一个ObjectFactory对象,二级缓存中有一个经过代理处理过的testA对象】


       -> 获取到对象之后,经过getObjectForBeanInstance方法,即FactoryBean的那一套拿bean的方法,获取到bean,也就是testA嘛。

       -> 然后回到testB里面的初始化方法,拿到了testA,将testA设置给了testB。好到这里testB的populate方法就执行完了,然后执行initializingBean。

       -> initializingBean,顺便也说一下这个吧

              先回调内置的Aware接口,顺序依次是BeanNameAware,BeanClassLoaderAware,BeanFactoryAware。第三个接口用的比较多哦

              回调所有普通BeanPostProcessor后置处理器的postProcessBeforeInitialization方法,

              回调afterPropertiesSet方法,如果bean实现了InitializingBean接口的话。

              回调自定义的初始化方法。

              回调所有普通后置处理器的postProcessAfterInitialization方法。
     
           这样testB其实就已经实例化,初始化,调用初始化方法了。
 
           现在流程还在testB的doCreateBean方法里面,继续走逻辑,
     
      -> 判断是否开启了提前暴露,即是否允许循环依赖。如果开启了的话,那么走getSingleton(beanName,false),[第二个参数控制是否开启那个代理处理过程]先从一级缓存中获取,显然

            获取不到并且在创建当中,继续到二级缓存中去,又获取不到,并且第二个参数限制了不从三级缓存中查找。

            然后由经过了查找beanFactory中是否配置了DestructionAwareBeanPostProcessor后置处理器,判断当前bean是否需要销毁,将单例bean放到beanFactory中的disposableBeans变

            量中,待容器关闭前销毁。

       ->这个时候testB已经设置了testA并且已经完成了bean的生命周期,这个时候还在getSingleton(beanName,singletonObject)方法中,在singletonObject = singletonFactory.getObject();

           这个步骤成功返回了testB。接着执行afterSingletonCreation(beanName),这是在bean成功返回之后执行的方法,把testB从singletonsCurrentlyInCreation移除掉。

         【这个时候的状态: 三级缓存中只有一个testB相关的一个ObjectFactory对象,二级缓存中有一个经过代理处理过的testA对象,一级缓存中还没有testA,testB。但是testB目前来看是已经创建好了的。】

          接着执行addSingleton(beanName, singletonObject);把testB放到一级缓存里面(因为testB已经完整了),把testB从三级缓存中移除掉(三级缓存的作用就是进行一个代理处理,testB已经经过了这个过程了,

          所以不需要第二次再经过了,况且本来就先查的一级缓存,所以空出空间来)

         【这个时候的状态: 三级缓存中没有,二级缓存中有一个经过代理处理过的testA对象,一级缓存中有testB。】

       ->现在testB的getSingleton(String beanName, ObjectFactory<?> singletonFactory)这个方法已经结束了,然后开始走FactoryBean取bean的那一套逻辑。

          此时观察AbstractBeanFactory中的doGetBean方法的每个情况下面都有一个bean = getObjectForBeanInstance(..)这样的步骤在后面,好的,现在这样的一个完整的testB已经返回了。
 
        ->好的,现在到了testA的设置属性的地方,这个时候是已经拿到了testB的,这个时候testA中就设置了testB的属性。

        ->然后同样是testA的initializing调用。

        ->然后是允许循环依赖,并且处于创建当中,续调用getSingleton(testA,false),先从一级缓存中找,没有并且正在创建当中,再从二级缓存中找,这个时候是有testA的,返回它

        ->afterSingletonCreation把testA从singletonsCurrentlyInCreation中移除掉,

        ->addSingleton(beanName, singletonObject);把testA放入一级缓存(因为此时A已经完整了),从三级缓存中移除(此时三级缓存是空的),二级缓存移除掉testA(testA此时从二级缓 存中移除掉)。

        ->调用getObjectForBeanInstance按照FactoryBean的规则拿bean

     
       到目前为止,testA和testB都已经到了一级缓存里面。

--------------------------------从上往下,顺序调用-----------------------------

AbstractBeanFactory # doGetBean

DefaultSingletonBeanRegistry # getSingleton(beanName,singletonFactory)

AbstractAutowireCapableBeanFactory # createBean(beanName,mbd,args)

AbstractAutowireCapableBeanFactory #doCreateBean

----------------------------------------------------------------------------------

          
我们看下最普遍被问到的一个问题,为什么spring要使用三级缓存来解决循环依赖的问题?

我觉得这个问题,首先应该从为什么要使用缓存谈起,我们注意到框架内部每次向beanFactory中使用getBean方法的时候,总是先找缓存,

然后如果没有,就创建这个beanName对应的bean,然后放到缓存中,所以第二次以相同的beanName去拿bean的时候,这个时候的缓存

中是有这个bean的,那么就不用再次创建这个bean了,这也是我们经常在代码里写的逻辑,因为这样就节省了创建重复beanName对应的bean的时间了。

好,这个时候,我们意识到了:我们应该要使用缓存了。

那我们现在假设只给一个一级缓存,即singletonObjects。那么对于一个bean的生命周期来说,分为好几个阶段,

一个是刚刚被实例化还未开始初始化,一个是实例化了并且也完成了初始化,spring容器正是将这两个过程拆分开来,并且在不同阶段应用各种干扰手段,给外界提供

修改正在创建的bean的各种各样的机会,这也是spring容器能很好的被扩展的原因,这也是spring要贯彻到底的,它必须这么做,才能提供给外界干涉创建bean的机会。

但是这样做,就一定会存在实例化状态和初始化状态,即成品和半成品这两种状态。那么此处假设spring的设计者只用一个缓存,缓存已经经过初始化的bean的话,似乎也可以,

但是,这里一旦出现循环引用,这种解决思路就废了,分析一下这个过程,首先去getBean(testA),然后发现testA依赖testB,注意这里没有把testA给存起来,因为testA还只是个半成品

然后就去getBean(testB),然后发现testB又去get(testA)。这样就出现了一个死循环。所以spring的设计者必须至少要使用二级缓存才能够解决这个循环引用的问题。

好,现在我们意识到了,我们应该要使用二级缓存了,才能够解决循环依赖的问题。

我们顺着刚刚的思路,先聊一聊使用二级缓存能否解决循环依赖的问题,这个问题的答案是肯定的。我们看一看分析的过程,这里我们依旧是把成品放到一级缓存里面,二级缓存我们来存半成品,

现在我们考虑一下循环引用:首先去getBean(testA),testA被创建了,但未初始化,注意,这里我们把testA放到二级缓存中去。好,现在开始初始化testA,发现testA依赖testB,然后就去

然后就去getBean(testB),然后发现testB又去get(testA),这个时候二级缓存就起作用了,因为我们把刚刚实例化的testA放到了二级缓存中去了,所以就拿到了testA,这样testB就顺利的完成了初始化,

然后就回到了tesetA的初始化过程中获得了testB,然后testA就可以完成初始化了,这样循环依赖就被解决了。这样,的确能够解决循环依赖,但不要忘了spring除了提供给我们ioc之外,

还提供了一个强大的aop的功能,那么如果采用二级缓存,能不能也符合aop功能的预期呢?这里先给答案,这里是不可以的!

要明白二级缓存为什么不能符合aop功能的预期,我们必须先看一看没有循环依赖的这种情况,即在一般情况下spring是在何处给我们创建的aop代理的,

我们可以通过debug的手段,可以知道spring是在实例化bean,完成populate属性赋值后,在initializingBean方法的调用中的调用完bean的初始化方法之后,会回调普通的BeanPostProcessor后置处理器

的postProcessAfterInitialization(bean,beanName)方法,返回代理,注意!这里是关键点,代理正是在此处产生,我们可以看到AbstractAutoProxyCreator的postProcessAfterInitialization(bean,beanName)

方法中有创建代理的逻辑。注意这个时候,initializingBean调用完了,这个bean的生命周期也就差不多可以说差不多走完了。在没有循环依赖的情况下,这样是没问题的,的确能够符合aop功能的预期,即在这里

spring容器为我们创建了这个bean的代理对象。

那么采用二级缓存,在出现循环依赖的情况下,会怎么样呢?我们继续来分析一下。这里先给个答案,是不行的,因为不能实现代理了。

继续走流程,testB是个切面,testA和testB都是组件,testA和testB相互依赖,我们希望用tesetB的before方法去切testA的一个say()方法,那么spring容器应该要为我们创建testA代理才对,

并且这里testA和testB相互依赖,即这里出现了循环依赖的问题,并且我们希望注入给testB的是testA的代理,而不是testA,这是我们想要spring为我们做的。我们来看spring是怎样做的,

首先getBean(testA),那么spring就开始创建testA的实例,还未初始化。这个时候我们势必要把testA创建的这个实例先放到二级缓存里面,方便这个刚刚被实例化的testA在接下来的初始化过程中,万一某个

需要被注入的属性又依赖了testA呢,这样就可以从这个二级缓存中获取了。好的,testA刚刚初始化完了,我们接着看testA的初始化过程,发现testA要注入testB,所以就去getBean(testB),好的。这个时候,

spring又去帮我们创建testB的实例,创建好testB的实例后,就要对testB进行一个初始化的操作,才能让testB完整,并且需要让testB这个bean走完它的一个的整个生命周期,这个在testA的populate方法中获取

testB这个bean的这个调用步骤才能返回,才能完成testA的属性赋值的操作。好的,因此testB就必须完成这个初始化的步骤,而testB要完成这个步骤,就必须注入testA,对吗?不对!!!这里是整个循环依赖的

最重要的一个关键节点了,一定要搞清楚这里,这里值得反复强调一下:还记得之前我们说的,出现了循环依赖,我们也想要spring为我们做的是什么吗?是的,我们希望在即使出现了循环依赖的情况下,

spring也仍然能为我们创建testA的代理供我们使用。但是现在看看testA目前还是个什么情况?testA还处于刚刚被实例化,为了完成初始化,又去getBean(testB),testB初始化完后,为了完成它的初始化,又去

getBean(testA),虽然testA这个实例现在是有了,但是,天哪!它还没有被初始化,它还在初始化的过程中,这该怎么搞?为了完成testB的初始化,我们想要获取的是testA的代理对象,而并非是testA这个bean。

但是容器中我们只有testA这个实例并且还处于初始化的过程中,原谅我把这个罗嗦了两遍。我们应该还记得我之前说的,在没有出现循环依赖的情况下,即在一般情况下,testA的代理是在什么时候产生的,没错,

一般情况下,testA的代理在testA实例化完,populate初始化完后,在initializingBean(beanName,bean,mbd)这个调用方法里:进行三大aware回调、

普通BeanPostProcessor的postProcessBeforeInitialization初始化方法的回调、初始化方法回调(里面又包括InitializingBean接口的回调和自定义初始化方法的回调)、

普通BeanPostProcessor的postProcessAfterInitialization()方法回调。

正是在initializingBean这个调用中的普通BeanPostProcessor的postProcessAfterInitialization()方法回调中,使用到了AbstractAutoProxyCreator这个后置处理器重写的postProcessAfterInitialization(bean,beanName)

中才创建的代理,也就是在一般情况下,如果要为某个bean创建代理的话,那么应该是要在它经历完实例化、初始化、并且回调完初始化方法之后,使用后置处理器的postProcessAfterInitialization这个方法去创建

它的代理,但是现在我们看看testA这个bean还处在什么状态,竟然还在初始化状态。现在我们遇到了这样的一个处境,我们想要testA的代理,但是如果按照一般情况下来说,应该要等testA完成初始化后,并且

回调完初始化方法之后,我们才能走AbstractAutoProxyCreator的postProcessAfterInitialization方法去获取testA的代理。可是现在testA还没完成前面的那些步骤呢,没完成前面的哪些步骤,在一般情况的逻辑下

,spring就不能为我们创建testA的代理了。也就是说在只有二级缓存的情况下,我们想要在出现循环依赖的情况下,想要在testB初始化过程中获取到testA的代理时,这是无解的,我们不可能获取到testA的代理!

那么该怎么办呢?因此,三级缓存就出现了,三级缓存的出现解决了这个问题,我们先来看下它的体系,把创建代理的逻辑除了在AbstractAutoProxyCreator的postProcessorAfterInitialization(bean,beanName)

方法中放了一遍,还有在AbstractAutoProxyCreator的getEarlyBeanReference这个方法中也放了一遍。我们可以看到,在AbstractAutoProxyCreator这个类中,这两个方法几乎是一致的,并且还使用了缓存,

也就是说,如果之前创建了某个bean的代理,那么就直接拿,就不用再次创建了。我们观察到AbstractAutoProxyCreator的里面有个getEarlyBeanReference(bean,beanName)方法这个方法是做代理逻辑的。

在AbstractAutowireCapableBeanFactory中也有一个getEarlyBeanReference(beanName,mbd,bean),而在这个方法里我们可以看到,它遍历所有SmartInstantiationAwareBeanPostProcessor,并且会

回调这种后置处理器的getEarlyBeanReference(bean,beanName)方法,而AbstractAutoProxyCreator的getEarlyBeanReference(bean,beanName)也正是因为实现了SmartInstantiationAwareBeanPostProcessor,

这个接口而重写的getEarlyBeanReference(bean,beanName)方法去实现自己获取代理的逻辑的。而且这个名字也是取得异常的明显,获取早期bean的引用,

这个方法存在的目的,就只是为了在出现循环引用的情况下,才会调用此方法,如果没有出现循环引用,那么就不会调用这个方法,理解这一点是关键!也就是说,你要通过实现SmartInstantiationAwareBeanPostProcessor

接口的方式并且配置给beanFactory,通过重写getEarlyBeanReference(bean,beanName)去告诉spring在遇到循环引用的情况下,该怎么处理这种情况!!!如果你配置它了,spring就会按照你的这个接口的实现逻辑去走。

也就是说,spring把这种循环引用的这种情况下的属性注入的决定权交到了使用这的手上,你既可以创建代理,走代理逻辑,也可以决定在出现循环引用时,就不创建代理。这将完全取决于使用者。spring正是设计了

这一个又一个的接口,让我们能够把自己想要的逻辑植入到spring框架中,然后由框架完成剩下的工作。spring通过提供给用户接口的方式,让使用者能够和spring框架进行一个交流。说到这里,其实整个逻辑就整

明白了,我觉得不可能再有其它的文章能把这个循环依赖和三级缓存的问题说的比这更详细了。好,现在我们已经对三级缓存在spring框架中的体系已经有了大致的认识。

好,现在我们有了三级缓存,那我们就来看下spring是怎样使用三级缓存,并且在存在循环引用的情况下,仍然能够提供Aop功能的。在文章的一开始,我们就已经详详细细的分析了,在不需要创建代理情况下,

spring是怎样使用三级缓存解决循环依赖的问题的,但是我们前面又刚刚说到其实二级缓存就已经可以解决不使用代理情况下的循环依赖的问题,到这里,我们再回过头来看下最初的那个问题:

为什么spring要使用三级缓存来解决循环依赖的问题? 这个问题的答案已经很明显了,因为spring除了要提供ioc的功能外,还要为使用者提供强大的aop功能,而在提供aop功能时,有一个致命的缺陷,那就是原本

创建代理的逻辑是在初始化完成后,才会去创建代理的,这种在没有出现循环依赖的情况下,spring是能够提供aop功能的,而一旦出现循环依赖,spring将不能在出现循环依赖情况下为我们提供aop的功能。这就是

spring采用三级缓存做这件事的根本原因。这件事就是为了在出现循环依赖的情况,spring将仍然能够给使用者提供aop的功能。好像罗嗦了不止两遍,因为我觉得有些东西值得反复咀嚼,才能真正的懂它这样玩的原因

懂了这个,我们再来走出现循环依赖的情况,并且我们希望也能够使用到aop的功能的流程。现在的场景是:testA与testB相互依赖,并且testB是个切面,我们希望用test的before方法去切testA的的say方法,那么

我们在在把testA和testB同样放到spring容器里面,并且在xml中配置了aop的相关设置,也就是给beanFactory配置了一个SmartInstantiationAwareBeanPostProcessor后置处理器,也就是前面提到的AbstractAutoProxyCreator

它以抽象类的方式实现了这个SmartInstantiationAwareBeanPostProcessor接口,具体的实现交给了AbstractAutoProxyCreator它的子类。我们既然要去切testA的say方法,那么我们spring势必要创建tesA的代理,

并且把这个代理注入给testB。

首先spring容器先去getBean(testA),然后testA被实例化了,注意这个时候testA这个对象已经被创建了,只不过还没有完成初始化。此时此刻,spring框架做了一件事,查询一下spring自己的配置,看beanFactory

有没有开启循环依赖(这个配置项可以被使用者修改),当然还有当前bean是不是单例的(这个条件说明spring仅支持单例情况下的循环依赖),如果开启了循环依赖,那么这个时候spring把当前刚刚被实例化的testA

对象(此时还不能称作bean,它还不完整,至少属性还没被赋值设置),包了一层包装成了一个方法里面的匿名内部类对象,并且还把当前的beanName,mbd,bean,也传到这个匿名内部类对象里面去了,这个

匿名内部类对象是ObjectFactory<T>的子类对象。这是什么意思呢?也就是说这个神奇的匿名内部类对象里面,它持有了当前testA的所有相关信息,你看beanName,好,名字有了。mbd,创建bean的源信息有了

bean,就连刚刚实例化出来的testA也有了,这就为之后的被调用埋下了最基础的条件,这时一定要记住这个匿名内部类对象是已经持有这些东西的了,嗯,一定要记住这个。然后回到刚刚说的,如果开启了循环依赖,

那么,就把这个匿名内部类对象,以键为beanName,值为这个匿名内部类对象,放到了三级缓存中,我再提醒一遍吧,这个匿名内部类对象是持有beanName,即testA的名字,mbd,即testA的源信息,

bean,即testA这个bean。然后根据流程来说,开始走testA的初始化,发现testA里面需要一个testeB,才能完成testA的初始化。所以就去getBean(testB),然后testB被实例化,然后这里打开了循环依赖的话,

还要把testB以匿名内部类对象的形式放到三级缓存中,然后根据流程应该要走testB的初始化。

而testB初始化,就需要testA对吗?不对!!!我们需要testA的代理,之前二级缓存就是因为解决不了这个代理问题。所以testB的初始化是需要testA的代理的,然后同样的去getBean(testA),我们应该还记得我刚刚

提醒的,我们刚刚实例化完testA后,是把testA包装成了一个匿名内部类对象存到了三级缓存中去了。这个时候的testA能够被拿到吗?按照源码拿的顺序来说,先拿一级缓存,一级缓存中没有,testA还不完整还没放进

一级缓存的资格,二级缓存中没有,有放进去的资格,但是我们应该还记得我们刚刚实例化完testA后,是立马把它放到了三级缓存里面去了(当然这是在开启允许循环依赖的条件下),压根就没放二级缓存。三级缓存、

中有吗?正好就有,此时我再加上一个条件哦,之前忘了说了,那就是testA这个对象在创建前,是会被放到正在被创建的set集合里面的,目的是为了标记正在创建的bean,在被创建好了之后,又会把它从这个set集合中

移除掉,所以当目前getBean(xxx)的xxx这个bean正好在这个set集合中,那么请问说明了什么问题?没错,出现了循环依赖的问题!!!循环依赖正是这样被检测出来的。好,依旧的回到刚刚那个问题,现在要getBean(testA),

发现现在要获取到的testA正好在这个set集合里面,伊,testA正在处于被创建的状态,说明之前早就调过了getBean(testA)这个方法了,现在是还没创建完(这里的没创建完指的是还没走完testA的生命周期),

没错,此时,出现了循环依赖满足了那个条件(目前要获取的这个bean正好在标记正在被创建的set集合当中)。所以这里才能去从三级缓存中根据beanName去拿之前存进去的匿名内部类对象,

注意这里一定是要满足-要获取的bean正好在标记整在被创建的set集合当中-这个条件,才会从三级缓存中去拿这个匿名内部类对象。因为这里满足了这个条件,所以就拿到了这个匿名内部类对象,应该还记得这个匿名内部类

对象还持有着什么吧。这个匿名内部类对象持有着testA相关的beanName,mbd,bean的三个东西,并且还有一个特殊的东西,刚刚故意没说,但前面有提到过,就是还持有了对AbstractAutowireCapableBeanFactory的

getEarlyBeanReference(beanName, mbd, bean)方法的引用。如果没有满足这个条件-要获取的bean正好在标记整在被创建的set集合当中-那么就不会那这个匿名内部类对象。因为这里满足了,所以拿到了这个匿名内部类对象

,然后就会调用这个匿名内部类对象的getObject方法,因为这个匿名内部类对象是实现了ObjectFactory接口的。现在,再来插一句哦,我们之前在AbstractBeanFactory中调用doGetBean的时候,

在判断完当前的mbd是单例的时候,然后也调用了getSingleton(beanName,()->return createBean(beanName, mbd, args);),这里先这样简化了这个表达式。所以spring把这种用法用了2遍,

一个是在AbstractBeanFactory的doGetBean方法中,在判断完是单例后,通过getSingleton(beanName,singletonFactory)方法的调用,创建的顺序逻辑交给了父类DefaultSingletonBeanRegistry,而把真正的getObject的方法,

又是通过重写这个getObject方法,以AbstractBeanFactory的createBean这个抽象的模板方法,委托给AbstractBeanFactory自己的子类去做真正的实现。

一个是AbstractAutowireCapableBeanFactory的createBean方法中调用doCreateBean方法里面,通过判断是否开启循环引用设置,来将这个匿名内部类对象放到了三级缓存里面,而调用时机是在发现了循环引用

才去获取这个存起来的匿名内部类对象。

好,回到主线剧情,我们以发现需要get的bean正好在正在被创建的set集合里面,发现了循环引用这种情况,然后才能去获取到这个匿名内部类对象,并且我们已经知道了这个匿名内部类对象已经持有着什么了吧。好,

然后,就会调用这个匿名内部类对象的getObject方法,刚刚已经说过了,这个匿名内部类对象是持有着对AbstractAutowireCapableBeanFactory的getEarlyBeanReference(beanName, mbd, bean)方法的引用调用的,

而匿名内部类对象的getObject方法,就是把持有的这个方法作为它的实现,所以就调用了AbstractAutowireCapableBeanFactory的getEarlyBeanReference(beanName, mbd, bean)的方法,从而就走上了,

AbstractAutowireCapableBeanFactory的getEarlyBeanReference(beanName,mbd,bean)方法的逻辑。并且我们之前是有看过AbstractAutowireCapableBeanFactory的getEarlyBeanReference(beanName,mbd,bean)方法的,

,它会找beanFactory中配置的SmartInstantiationAwareBeanPostProcessor后置处理器,然后回调这个后置处理器的getEarlyBeanReference(bean, beanName)方法。我们恰恰在AbstractAutoProxyCreator重写

SmartInstantiationAwareBeanPostProcessor这个接口的getEarlyBeanReference(bean,beanName)方法中,看到了与AbstractAutoProxyCreator的postProcessAfterInitialization(bean,beanName),几乎一样的实现方式。

也就是创建代理的实现方式。这里就已经验证了,我之前说的:spring正是以这种接口的方式,让我们自己去实现自己想要的逻辑,然后配置给spring,spring就会把我们的逻辑植入到spring中。而这里,植入的逻辑

就是告诉spring应该怎样去解决,出现循环依赖时,是不是应该要创建代理?尽管这个bean目前还不算完整(相比于一般情况下的代理的情况,之前讲过一般情况下,到代理这一步这个bean是已经完整了,这里的这个Bean还

未被初始化呢)。甚至在这里,你可以直接的返回一个null,告诉spring在出现循环依赖的情况时,spring你就不用处理了,直接给个null,如果是这样的话,testB中就会注入一个null。

这样应该就算是把三级缓存和循环依赖的这个问题彻底讲清楚了吧。好,接着来,那么到这里就已经可以返回了testA的代理了,这个代理,就是我们配置的SmartInstantiationAwareBeanPostProcessor

这种后置处理器的实现类的getEarlyBeanReference(bean,beanName)方法返回的对象,至于这个返回的对象是不是代理,完全取决于使用者的逻辑的。这里假设是代理,那么我们是不是就拿到了这个代理,

拿到这个testA的代理后,把testA的代理放到二级缓存中去(后面我们再看下为什么要放到二级缓存中去),然后在三级缓存中把包装了testA的那个匿名内部类对象给移除掉。这样就返回了testA的代理,那么接下来,

testB的初始化过程中,此时终于获取到了testA的代理对象了,就达到spring在出现循环依赖的情况下也能提供aop功能的预期了。然后testB就可以顺利的注入testA的代理,然后testB就完成了初始化,按照流程来说,此时

,还要调用initializingBean(beanName,bean,mbd),我们再复习一下,首先是testB的三大Aware接口的回调,普通BeanPostProcessor的postProcessBeforeInitialization(bean,beanName)方法的回调,初始化方法的回调,

初始化方法的回调里面又包括spring内置的InitializingBean接口的回调和自定义初始化方法的回调,然后是普通BeanPostProcessor的postProcessAfterInitialization(bean,beanName)方法的回调,我们还记得就是

在这个回调里面,可以产生代理对象的哦。好这个bean的生命周期走到了这里,已经差不多了,然后逐层返回,返回到DefaultSingletonBeanRegistry的getSingleton(beanName,singletonFactory)的singletonObject = singletonFactory.getObject();

这一句调用,已经返回了testB对象,这个testB对象是已经完整的了,那么它是不是应该被放到一级缓存中去呢?下次再要获取testB,就不用又这么大费周章的创建了,spring接着在DefaultSingletonBeanRegistry的getSingleton(beanName,singletonFactory)

这个方法里面,把它从正在创建的bean的set集合移除掉,然后把这个bean和对应的beanName放到一级缓存中,并且从二级、三级缓存中移除,但是我们知道,其实二级缓存中压根就没有存testB相关的东西,三级

缓存中倒是存了一个testB相关的匿名内部类对象。那么现在的三个缓存中只有一级缓存中有testB了,另外两个就被移除掉了。然后这个DefaultSingletonBeanRegistry的getSingleton(beanName,singletonFactory)的

这个方法就走完了,然后走FactoryBean获取bean的规格去获取到bean,最终的testB就被返回了。别忘了,现在流程就还在testA的初始化过程中获取testB这里,好的,

回到testA的初始化过程,此时我们应该清楚testA的代理对象已经放到了二级缓存中,而testA获得了testB,然后把testB注入完成后,testA的初始化过程就完成了。这个时候的testA是已经完整了,此时应该要明白,

testA是已经完整了,testA的代理呢?也就是说testA和testA的代理的这种关系是由使用者来决定的,spring为我们提供的jdk动态代理和cglib动态代理这两种技术,就是为了解决testA和testA的代理之前的关系的技术的,

,然后继续走完testA的流程,开始回调testA的初始化方法,而并不是testA的代理的初始化方法。testA此时初始化完了,那么要开始调用initializingBean(beanName,bean,mbd)方法了,注意此时还是testA而并非testA

的代理哦,然后回调testA的三大aware接口,应用普通BeanPostProcessor的postProcessBeforeInitialization(bean,beanName),我们最好还是看下这个方法比较好,传进去一个bean,并且把beanName也传了进去

这让我们想到了什么,是的,这个传进去的是testA说不定这个方法返回的就是testA的代理。的确,确实可以这么去做,但是我们可以看到AbstractAutoProxyCreator它并没有这么去做,至少没有在这个方法里去做,

但是我们,哈哈,我们自己配置的话,就可以这么去做的。现在我们还是按照常规的来吧,嗯,把源对象返回,即testA返回,然后回调testA的初始化方法,比如实现InitializingBean接口的afterPropertiesSet方法回调呀,

自定义方法回调啥的。此时又来回调普通BeanPostProcessor的postProcessAfterInitialization(bean,beanName),我们应该还记的,在一般情况下,即没有出现循环依赖的情况下,如果需要spring创建代理的话,

spring是在何处创建的代理,没错,就是这个方法,这个方法看上去也是传进去一个bean和beanName,返回一个对象。这正好就可以用来返回代理了,我们看看AbstractAdvisorAutoProxyCreator的这个

postProcessAfterInitialization(bean,beanName)方法,进去里面看看,就是我们之前看的创建代理的逻辑,只不过,之前已经被创建过了,所以这里可以根据bean的class和beanName生成了一个cacheKey,

这个cacheKey之前已经生成过对应的testA的代理了,所以就可以从缓存中直接拿到这个testA的代理了,嗯,传进来是testA,出去之后就是testA的代理了。此刻,我们应该就能明白spring正是在此处,来了个偷天换日,

那么现在,我们应该能对很多调用顺序有了更加深刻的认识了。testA的代理在此处正式代替testA登上了spring的舞台,而且这个testA代理就是之前我们第一次创建的testA的代理,因为是从缓存中找的嘛。

经过了initializingBean的调用之后,testA的代理就出来了,然后还有个什么问题,testA这个bean还在二级缓存中呢。现在的调用链实在是太长了,有点(((φ(◎ロ◎;)φ)))晕,

但是我们还能找到最初是在DefaultSingletonBeanRegistry的getSingleton(beanName,singletonFactory)中,本来是使用singletonFactory的getObject方法去触发AbstractAutowireCapableBeanFactory的

createBean(beanName, mbd, args)方法的,然后我们整了一大圈的逻辑,最终返回了testA的代理,在singletonFactory的getObject方法调用中返回了testA的代理之后,因为这个时候testA的已经获取完了嘛,

所以把它从正在被创建的bean的singletonsCurrentlyInCreation的set集合中把它移除掉,并且把这个testA的代理对象放到了一级缓存中,并且根据beanName,把二级缓存、三级缓存中的testA给移除掉,这样

testA的代理就顺利的到了一级缓存中,下次去getBean(testA)时,就会直接在一级缓存中找到,并且找到的是testA的代理。

似乎到这里已经算完了,但是还记得我们刚刚又提出了一个问题:testA的代理放到二级缓存中去?即,我们在已经获得了testA的代理,为什么要把testA的代理放到二级缓存中去,其实本质就一句话,已经创建了testA

的代理,还有必要再走一遍获取代理的逻辑吗?完全没有必要嘛,可是这里不是只获取了一遍吗?是的,这里是只获取了一遍,但是呢,我们现在说的是两个对象之间的相互循环依赖,如果你的这个循环依赖比较的复杂,

就有可能出现要第二次去获取testA的代理,那就完全没有必要嘛!直接从二级缓存中直接拿就好了。这样就可以解释那里为什么要在产生testA的代理后就就把testA的代理放到二级缓存中去了。为了严谨性,我们还是

先yy一遍流程,毕竟要说出一个道理出来。现在我们假设,A和B相互依赖,A和C相互依赖,这样就出现了2个循环依赖,然后我们需要spring为我们创建A的代理,初始化顺序有可能是先getBean(testA),

然后testA被实例化,因为打开了循环依赖的开关,所以此处把包装了testA的匿名内部类对象放到了三级缓存中去,接下来开始testA的初始化,发现testA是依赖testB的,所以就去getBean(testB),然后testB就被实例化了

然后把包装了testB的匿名内部类对象放到了三级缓存中,然后开始testB的初始化,那么又发现testB是依赖testA的,所以,就去getBean(testA),testA正在创建当中,并且它的匿名内部类对象已经放到了三级缓存里面,

那么此处就走了从三级缓存中获取了testA的一个代理(这里就不解释了,前面说过了),testA的代理一旦被创建,还有必要再走一遍创建testA的代理逻辑吗?可以走,但是完全没有必要,你的传参都没变,所以我们可以

看到在DefaultSingletonBeanRegistry的getSingleton(beanName,allowEarlyReference),(第二个参数是true,即允许从三级缓存中去拿东西),这个方法里面在从三级缓存中拿完testA的代理后,立即把这个testA的代理

就放到了二级缓存里面,并且从三级缓存中把包装了testA的匿名内部类对象给移除掉了,为什么要从三级缓存中移除掉,就是因为testA的代理既然产生了,就不需要再此调用三级缓存产生的代理的方法的逻辑了。

然后按照FactoryBean接口获取bean的方式,把testA的代理返回去了,然后testB拿到testA的代理,testB就可以顺利完成它的初始化了,

然后调用initializingBean,然后就是那三大aware回调普通BeanPostProcessor的postBeforeInitialization回调,初始化方法回调,普通BeanPostProcessor的postAfterInitialization回调,

然后也是按照FactoryBean接口获取bean的方式,然后又返回到DefaultSingletonRegistry的getSingleton(beanName,singletonFactory)的这个singletonObject = singletonFactory.getObject();调用步骤返回了testB,

其实感觉就是在这里完成了核销,我指的是创建单例bean的操作都由我这里触发,然后,创建好了之后,继续回到我这里完成一个核销的操作,目的是要告诉spring这个对象已经创建完了,

我们可以看到同样是在这个方法里,把创建完的对象放到一级缓存了,并且从二级缓存、三级缓存中根据名字把这个bean相关的都移除掉,还有这个bean已经不处于创建中了,

所以又把这个bean从正在被创建的singletonsCurrentlyInCreation的set集合中把它给移除掉。那么到这里testA的初始化过程中获取testB就成功了,然后testA的初始化过程又发现,需要getBean(testB),

现在我们还记得哦,此时,testA还在初始化的过程当中,并且testA的代理已经产生过了,并且放到了二级缓存里面,好,现在继续getBean(testB),然后testB就被实例化,并且也放到了三级缓存里面,

然后进行testB的初始化,发现testB也依赖了testA,那么就去getBean(testA),刚刚已经提醒了,testA的代理已经放到了二级缓存里面,所以直接去二级缓存中去拿就好了,

没有必要再走一遍创建testA的代理的逻辑了,更何况spring在第一次获取到spring的代理之后,即立即把三级缓存中testA对应的匿名内部类对象给删除掉了,spring自己也觉得没必要留着它了。

那么此刻,二级缓存的作用暴露无遗了,它就是用来存第一次代理逻辑产生的对象的。

让spring在复杂的循环依赖中第二次获取同样的代理对象时,直接从二级缓存中去拿,不用再去走一遍创建代理的逻辑,从而节省了时间,这个问题就算完了。

到现在为止,在出现循环依赖的情况下,testB和testA的代理都顺利的加入到了一级缓存当中去,并且能够为我们提供aop的功能。

现在我们再来简单的说下,spring的循环依赖的默认配置,spring默认是把循环依赖的开关是打开的,我们可以看到在AbstractAutowireCapableBeanFactory这个类里面的allowCircularReferences属性是true,

即spring默认打开的。当然我们也可以把它给关闭掉,那么关闭掉会出现什么样的情况呢?相信这是大家比较关心的。如果关闭的话,那么spring在刚刚实例化一个bean的时候必然就不会把它放到三级缓存中去,

那么一旦出现循环依赖的情况,那么这个过程将无法解决。

我们可以这样试着玩一下:

xml中先这样配置:这里把循环依赖都关闭掉,还有aop也不打开。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="testA" class="com.zzhua.circularDependency.TestA">
        <!--<property name="testB" ref="testB"/>-->
    </bean>

    <bean id="testB" class="com.zzhua.circularDependency.TestB">
        <!--<property name="testA" ref="testA"/>-->
    </bean>

    <!--<aop:config>
        <aop:aspect ref="testB">
            <aop:before method="before" pointcut="execution(* com.zzhua.circularDependency..*(..))"/>
        </aop:aspect>
    </aop:config>-->

</beans>

public static void main(String[] args) {
        // AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class);
        // context.close();

        /*ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-circle.xml");
        TestA testA = context.getBean(TestA.class);
        testA.testA();
        context.close();*/

        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        beanFactory.setAllowCircularReferences(false);

        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions("classpath:spring-circle.xml");

        beanFactory.preInstantiateSingletons();
        TestA testA = (TestA) beanFactory.getBean("testA");
        testA.testA();

        TestB testB = (TestB) beanFactory.getBean("testB");
        testB.before();
        
}
这样就可以顺利的打印出:
TestA构造器调用...
TestB构造器调用...
testA...
before....

但是你只要打开循环依赖,就是被注掉的属性赋值的那两个地方,就会报如下的错误,因为这样根本就解决不了循环依赖的问题。
....
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testA': Requested bean is currently in creation: Is there an unresolvable circular reference?
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testB' defined in class path resource
...

我们还是追踪源码,首先是getBean(testA),然后就需要创建testA,在创建前把testA添加到了正在创建的set集合里面。然后实例化testA,然后由于没有打开循环依赖,那么就会直接走testA的初始化过程,

初始化testA的时候,发现testA是依赖testB的,然后就去getBean(testB),然后就需要创建testB,在创建前把testB添加到了正在创建的set集合里面。然后实例化testB,然后由于没有打开循环依赖,那么就会直接

走testB的初始化过程,发现testB是依赖tesetA的,所以就去getBean(testA),然后调用AbstractBeanFactory的doGetBean方法,依然先查缓存,缓存里面没有,然后就要走创建嘛,就到了

DefaultSingletonBeanRegistry的getSingleton(beanName,singletonFactory),在创建之前又准备要把testA加入到正在创建的set集合里面,但问题是能加进去吗?之前的第一步就已经将testA加入进去了,所以

现在想加进this.singletonsCurrentlyInCreation这个set集合中去,就会失败,一旦失败,就会抛出BeanCurrentlyInCreationException这个异常。我们可以看下这个方法。

protected void beforeSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
                      throw new BeanCurrentlyInCreationException(beanName);
         }

也就是spring通过这个set集合的添加当前正要被创建的对象,如果再次创建同样的,那么就会失败。那为什么开启了循环依赖这个设置就可以了呢?因为一旦开启循环依赖,那么我们在刚刚创建完实例,还没初始化

之前,把这个刚刚被创建的实例,包装成了一个匿名内部类对象放到了三级缓存中去,而在下一次getBean的时候,就会doGetBean,就会从三级缓存中找到这个被包装的匿名内部类对象,那么就不会再次走它的

创建流程,从而避免了抛这个异常。spring通过在实例化和初始化之前设置一个循环依赖的开关,使用三级缓存解决了不仅解决了循环依赖的问题(尽管二级缓存就足以解决循环依赖问题,但是不能解决提供aop功能的问题)

也解决了aop的问题。

好的,整个spring的循环依赖和三级缓存之间的关系已经彻底讲清楚了,从为什么要使用缓存,到使用一个缓存,到两个缓存,到三个缓存,以及从ioc和aop功能的角度分别去分析了这个问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值