什么是循环依赖?
之前提到了在进行创建单例Bean的时候有个类参数singletonCurrentlyInCreation,这个参数是用来记录当前正在进行实例化的beanName。此外还定义了三个存放不同实例的缓存:
一级缓存:singletonObjects,用来存放以beanName为key,对应已完成bean生命周期的bean实例作为value;
二级缓存:earlySingletonObjects,里面存放的都是提前暴露的单例实例,此时bean的生命周期还未结束;
三级缓存:singletonFactories:用来存放以beanName为key,对应的BeanFactory为value;
我们先创建如图的两个类,除了各自引用外再添加一个参数,用来跟踪这个bean的生命周期进行到哪一步了,并重写toString()方法,如图1、图2:
执行结果如图3,但是按照我们写的自定义Spring容器,会出现A和B不断的相互引用,最终耗尽了服务器资源,这就是循环依赖,Spring就是利用上面几个参数来解决循环依赖的问题。
Spring如何解决循环依赖?
1、在getBean操作的时候,进入doGetBean方法,执行getSingleton(beanName)方法,来尝试从三个缓存中拿到对应的实例,A_BeanName刚进来的时候三个缓存中都没有A_BeanName的值,在singletonCurrentlyInCreation中也没有正在实例化的beanName,说明目前这个bean还未被创建,如图4:
2、由于循环依赖只发生在单例的情况,那么直接看图5:
先关注下getSingleton操作,和上面获取实例的只是个同名方法而已,进来一共可以分成四部分,如图6:”beforeSingletonCreation(创建实例之前的操作)、getObject(创建实例)、afterSingletonCreation(创建实例之后的操作)、addSingleton(如果是新生成的单例,把他加进缓存)。beforeSingletonCreation会把当前的beanName放进singletonCurrentlyInCreation中,如图7。在调用getObject的时候会调用到匿名类的createBean方法来创建实例。
3、关注doCreateBean,首先调用createBeanInstance,本次采用的是无参构造方法instantiateBean,把A包装成A_BeanWrapper,具体的封装情况参考上节的getBean流程。通过断点可以看到此时A_BeanWrapper已经包含了B,但是没有A的其他属性,B属性也为null,说明A并没有创建完成,此时B进行未进行依赖注入,如图8:
4、earlySingletonExposure参数代表是否能进行提前暴露,也可以从这看到提前暴露的条件:单例、允许循环依赖、当前beanName正处于正在创建Bean的列表中。如图9:
此时的getEarlyBeanReference方法是用来收集BeanFactory实例的,从实现方法里的getCacheKey中可以看到,工厂Bean前缀特有的&标识,如图10和图11:
然后调用addSingletonFactory往三级缓存中添加A_BeanName和A_BeanFactory,如图12:
5、通过populateBean进行依赖注入,此时又是一个BeanPostProcessor的应用,postProcessProperties方法对@Autowired方法进行扫描,如图13:
findAutowiringMetadata返回的InjectionMetadata参数中的injectedElements参数就是B,如图14:
继续调用其inject方法,关注element.inject方法,由于@Autowired注解是放在参数上的,因此访问AutowiredFieldElement中的inject方法,调用resolveFieldValue,到beanFactory.resolveDependency,到doResolveDependency,到findAutowireCandidates方法获取到持有@Autowired注解的B,除了往autowiredBeanNames中添加对应的beanName外,调用descripter.resolveCandidate方法,进去发现是一个B_BeanName的getBean操作,如图15、16:
6:、和A一样,重复之前的实例化流程,在singletonCurrentlyInCreation中添加B_BeanName,通过addSingletonFactory往三级缓存中添加B_BeanName和B_BeanFactory,然后通过populateBean操作,最终通过getBean方法对A进行实例化。
7、又再次来到了第一步的getSingleton方法,此时singletonCurrentlyInCreation中已经有A_BeanName,虽然在一二级缓存中没有对应的实例,但是三次缓存中有A_BeanFactory,可以通过getObject拿到提前暴露的实例,然后把它放进二级缓存,打断点可以看到此时的A_SingletonObject中的B类属性为null,如图17::
但是此时走的就不是else的路线了,关注getObjectForBeanInstance方法,最终也是返回一个对象实例,如果是工厂实例,则会调用其getObject方法生成对象实例返回,期间并没有对其余属性进行诸如,我们在doGetBean的return的返回值中可以看到,如图18:
8、步骤2进行的getSingleton方法在getObject的方法终于有返回了,如图19,返回的是B的实例,可以看到生成的B实例中已经包含了testB属性,以及提前暴露的A属性。
然后再进行afterSingletonCreation方法,把B_BeanName从singletonCurrentlyInCreation中移除,如图20:
并通过addSingleton,把B_BeanName和B实例放入一级缓存后返回,此时A属性中的B属性依旧是null,如图21:
9、从上面的流程看,B实例化是由A的实例化中B属性的依赖注入触发getBean完成的,现在B已经被实例化了,因此A的B属性也可以完成依赖注入了,这样A中的B属性也有值。由于B的属性指向了A实例的堆空间,所以B类中的A属性也被赋予值了,如图22:
至此,Spring循环依赖的流程已经结束了。
自定义Spring容器代码地址:https://github.com/LuoChen1996/identitify_spring.git
Spring源码测试代码地址:https://github.com/LuoChen1996/my_spring.git