本篇笔记为上一篇《Spring IOC学习笔记之bean的生命周期》中的第5步的验证。
第5步:如果有循环依赖的情况,则提前暴露一个bean工厂,放到二级缓存中
首先介绍一下spring的三个缓存:
singletonObjects:springBean的单例池,一般称为一级缓存。
singletonFactories:单例工厂的缓存池,一般称为二级缓存。
earlySingletonObjects:未完成属性注入的对象,(一般是由单例工厂生成对象后放入其中)
/** Cache of singleton objects: bean name to bean instance. */
//单例对象的缓存池,key为beanName,value为bean实例
private final Map singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
// 单例工厂的缓存池,key为beanName,value为bean工厂
private final Map> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
//早期单例对象的缓存池,key为beanName,value为反射创建出来的对象的实例(还为完成属性注入)
private final Map earlySingletonObjects = new HashMap<>(16);
/** Set of registered singletons, containing the bean names in registration order. */
// 存放已经注册(实例化)了的单例对象的beanName。
private final Set registeredSingletons = new LinkedHashSet<>(256);
开始测试:
修改测试代码:在TestB类中依赖注入TestA,TestA类中依赖注入TestB。
找到doCreateBean方法断点:
可以看到此时testA中还没有注入testB
从截图中可以看到earlySingletonExposure的值为true,存在循环依赖。
进入if语句执行addSingletonFactory:
进入方法查看:
可以看到在singletonFactories集合中放入一个key为teatA的单例工厂singletonFactory,这个singletonFactory对象中包含了一个TestA对象,但是这个TestA对象中的xxB的值为null,说明还没有注入TestB。
跳出addSingletonFactor方法:
执行到完成属性注入的populateBean方法:
当执行到AutowiredAnnotationBeanPostProcessor这个后置处理器的时候:
进入这个后置处理器查看:
发现这个TestA中需要注入一个TestB
调用inject方法进行注入:
此时要为TestA注入一个TestB,但是现在还没有TestB:
查看调用链,此时是在postProcessPropertiesfang方法中调用inject
进入inject方法:
调用了另一个inject方法(这两个方法只是名称相同,并不是同一个)
进入这个inject方法:
此时要为testA注入一个名称为xxB,类型为com.salulu.test.TestB的值。
调用resolveDependency方法:
resolveDependency方法中调用doResolveDependency方法
此时spring是要为testA这个bean,注入一个名为testB的bean,执行descriptor.resolveCandidate(autowiredBeanName, type, this)方法:
可以看到Spring要去beanFactory中获取名称为testB的bean,(创建testA这个bean,需要依赖testB,所以现在要去获取testB这个bean)
进入getBean方法:
调用了doGetBean(),这个方法很熟悉,之前创建testA的时候就是调用了doGetBean这个方法:
查看调用栈:
但是testB是获取不到的,因为现在还在创建testA的过程中,所以要创建testB:
再次进入doGetBean方法:
此时testA对象和testB对象都已经创建,但是两个对象都还没有完成依赖注入:
进入addSingletonFactory方法:
回想一下,之前创建testA的时候,也进入过这个方法,并且把testA的单例工厂对象放到了singletonFactories这个集合中:
下面要对testB进行属性注入:
已知testB依赖了testA,但是testA还在创建过程中
进入populateBean方法:
运行到要调用AutowirrtAnnotationBeanPostProcessor类的后置处理器:
进入这个后置处理器方法:
这个方法有点眼熟,因为之前为testA注入属性的时候也调用到了这里
继续往下执行:
需要在testB中注入testA,但是现在testA还在创建过程中:
看调用链:
在第一次调用doGetBean("testA")之后,经过和很多步骤:要实例化testB,testB也要进行依赖注入
继续往下看,会不会无限循环了呢,因为现在调用到了testB中要注入testA,但是现在是testA的创建过程中,会不会再去创建一遍testA呢??
这里中间的几步都是与之前的过程都是重复的,这里就不截图了,直接到getSingleton方法:
inject方法要为testB设置一个值为的testA属性,需要到单例池中取
现在要去单例池中获取testA,但是testA不在单例池中
但是可以从二级缓存singletonFactories中获取到一个testA的单例工厂,所以并没有死循环。
单例工厂中有testA对象
注意getSingleton方法中的这两行代码:
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
把testA从单例工厂池中移除,放入earlySingletonObjects这个三级缓存中(主要作用就是性能优化)
因为获取到了testA,此时调用field.set(bean,value),为testB注入testA
此时testB将要返回,回到testA的依赖注入过程:
testB已经返回,testA中也可以注入testB
到此,解决了循环依赖。