现在有TestA类和TestB类,TestA持有TestB的引用,TestB持有TestA的引用,这就是循环依赖。如果没有Spring,我们又是如何去解决循环依赖呢。
spring解决循环依赖也是如此,首先暴露一个未初始化的实例TestA放到缓存中,创建TestB的实例时,获取的是TestA的未初始化对象,TestB创建完成以后,将TestA进行初始化,由于TestB中TestA的引用和TestA是一样的,TestB中的属性也是完全的初始化的。
Spring的三级缓存,DefaultSingletonBeanRegistry中存在三个Map,用作三级缓存。
(1)一级缓存:singletonObjects ,用于保存BeanName与创建bean实例。
(2)二级缓存:earlySingletonObjects ,用于BeanName与创建bean实例,与singletonObjects 不同的是earlySingletonObjects 中存放的bean是一个未初始化的bean。
(3)三级缓存:singletonFactories ,用于存放BeanName与bean工厂。
大致分为三阶段:实例化,填充(属性),初始化。
其中getSingleton方法被多次调用,它是一个重载方法。在创建bean之前,都会先去从缓存中获取,如果没有获取到,则创建以后放入缓存中,这种方式是非常惯用的,具体的调用流程:
(1)分别尝试从一级,二级,三级缓存中获取BeanName对应的bean,如果三级缓存中有,则将三级缓存中的对象放到二级缓存中,清除三级缓存。
(2)在初次加载TestA对象时,缓存中是没有任何TestA的bean对象。需要去创建一个TestA的bean,在创建之前,需要对bean进行标记(正在创建,防止重复创建)。这时也会用到getSingleton方法去创建TestA的实例。
(3)在TestA完全创建以后会放到一级缓存中,同时清空二三级缓存。
(4)在TestA进行实例化完成以后,将TestA对应的ObectFactory(对象工厂,三级缓存的值)放入三级缓存中,然后填充TestA的属性;
(5)这时候发现持有TestB的引用,进而创建TestB的bean(重复TestA的创建过程),TestB进行属性填充时会去从缓存中获取TestA的bean;
(6)此时的TestA是一个未初始化半成品存在于三级缓存中,当TestB属性填充完成以后,会从将TestB的bean放入一级缓存中,并从二三级缓存中删除TestB;
(7)继续填充TestA,并完成初始化后将TestA的bean放入一级缓存中,并从二三级缓存中删除TestA。
有一个值得注意的地方时,在创建普通bean(没有代理)的时候,二三级缓存其实没区别,这个匿名内部类ObjectFactory就是三级缓存中的值,用于获取被代理后的bean,没有进行代理时,二级缓存与三级缓存就是一样的。
在多线程环境下创建bean的过程,使用三级缓存是非常有必要的事情。