循环依赖其实就是循环引⽤,也就是两个或者两个以上的 Bean 互相持有对⽅,最终形成闭环。⽐如A 依赖于B,B依赖于C,C⼜依赖于A
Spring支持的循环依赖场景与不支持的循环依赖场景:
a,不支持单例Bean构造参数循环依赖,不支持prototype原型bean循环依赖。
b,支持单例并通过Set方法或者@Autowired注解进行循环依赖的循环场景。
描述之前先来看一下Bean实例化注入流程图:
流程图中的加载步骤这里不做具体的分析,详情之前的文章中有做过了源码剖析,传送门在这里Spring IOC容器源码剖析。而下面这张图是关于循环依赖解决的流程图。
Step1: 生命周期图中,Bean实例化后,对于一些属性或者实现方法并没有进行加载或执行,这里的Bean是一个初期的Bean,不是最后注入使用的Bean。而BeanA 则是在创建的过程中发现自己依赖B,会先将实例化后的Bean存入到三级缓存(singletonFactory)中,这里的BeanA就是一个不完全的Bean,然后因为要填充Bean B则去同时实例化Bean B。
Step2: 实例化Bean B的过程中,也发现依赖于A,这时候就会去三级缓存中去取这个未成型的Bean A,存入到属性中去,然后将Bean B存入到二级缓存(earlyFactory)中去。
Step3:Bean B创建完成后则会从二级缓存中升级到一级缓存,也就是最终成型使用的缓存区域,即之前提到过的DefaultListableBeanFactory对象中。
Step4:Bean B实例化完成后,那么Bean A的实例化流程中则能从缓存中获取了,这时候再开始实例化Bean A,然后升级到一级缓存中去。
A,入口:
类实例化入手,那么还是从finishBeanFactoryInitialization方法开始。通过Debug还是从实例化对象的方法中进入, 然后依旧来到doGetBean中,然后去从三级缓存中取。 但是这时候缓存中肯定不存在,判断进入else,然后判断是否是单例,进入到createBean中去创建BeanA。
☆ createBean中会跳转到doCreateBean中去创建Bean,到这里回去做判断,是否单例,是否开启循环依赖,是否是正在创建的类,到这里位置生命周期中的BeanA初步实例化已经完成。
B,初步实例化的存储:
继续进入该方法,会存入到三级缓存中,二级缓存这时候其实是没有的,所以remove并没有处理掉什么,这次的主要目的主要在于将BeanA存入到三级缓存中。
然后出来后继续往下走,到属性填充,这里的循环依赖,类实例作为属性存入自然是作为属性依赖处理,点进去,根据Debug进入到applyPropertyValues方法,这里就是处理属性依赖的地方。
C,填充类引用属性入口
点进去只有,然后debug到这里去处理类属性的引用值,也就是上面流程分析提到的,去拿BeanA中的作为属性的BeanB的值。这里为重点方法,点进去继续跟踪会发现到判断中直接返回了。
返回了那就继续跟踪resolveReference方法,然后会执行到getBean方法,这里用于属性引用为类时的装载,跟进去继续看会发现我们又重新回到了getBean方法。
重新回到这个方法中可以发现,缓存中也没有需要的BeanB实例,判断else继续往下走, 按照上面实例化的过程又会回到doCreateBean方法中去,在这里对Bean B进行实例化。 然后再继续往下走,会给BeanB去填充属性。还是依旧按照前面的流程会执行一遍然后回到doGetBean。
然后再次回到该方法从缓存中去拿,这次一级二级缓存中依旧没有,但是在三级缓存中是存在BeanA的!,存在这里就要存入到二级缓存缓存中,然后从三级缓存中清除。
缓存中取到了数据之后,那么退出该方法之后,则不再重新进入到else判断中。到这里为止其实BeanB就已经属性装配完毕了!
上面说到的Bean B装填完毕了,对于B的实例化流程则结束了,最后到getSingleton方法中的finally方法,注意这里的addSingleton方法。点进去之后就如同上面的流程说到的,会将装配好的BeanB存入到一级缓存中,同时对二级三级缓存在做一次删除操作。然后继续返回就会回到BeanA填充属性获取返回值resolvedValue的地方,这里则为实例化完成的BeanB。到此循环依赖问题则已经解决。