spring循环引用-笔记
创建两个类
package com.hkdpp.springdemo.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Test1 { @Autowired
Test2 test2 ; }
package com.hkdpp.springdemo.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Test2 { @Autowired Test1 test1 ; }
步骤:
默认没有延迟加载,容器启动时创建bean
注意:容器里放的永远都是bean,不是对象,对象可以理解为是bean的一个半成品。只有spring创建bean的过程结束了,容器里存放的才有这个bean。
1、spring容器启动时初始化bean,启动时最重要的方法refresh()。首先,在obtainFreshBeanFactory();中创建所有对象的BeanDefinition,在创建bean之前,
先去单例池(spring容器)获取,第一次发现返回null,然后开始先创建对象的实例(并不是springbean),但是在创建之前有一步操作,就是要把test1实例放入到一个set集合中(在获取不到bean的时候有一个判断条件就是此对象是不是正在创建)继续往下走,有个判断
一个是看是否支持循环依赖(默认是支持),如果支持,就会把test1实例封装成一个Object放入到二级缓存中(单例工厂)
,然后经过一系列的后置处理器,对实例进行一些改造。
2、其中有一个方法是填充属性值用的,也就是要依赖注入,test1类里有test2属性。要得到test2的bean,还是要先去容器里看有没有,此时没有。
3、开始创建test2的bean,一样的流程,还是先创建对象实例(并不是springbean),在创建之前有一步操作,就是要把test2实例放入到一个set集合中,
看是否支持循环依赖(默认是支持),如果支持,就会把test2实例封装成一个Object放入到二级缓存中(单例工厂)
中间又走到填充属性的方法,此时test2类里发现有test1。
4、然后去容器里获取test1的bean,也是返回null(容器里获取的是bean,不是对象)
但是此时的二级缓存和set集合里已经有了test1和test2两个实例对象。表示正在创建过程中。
5、返回null之后,有个判断表示是否正在创建此对象,也就是set集合里有没有这个对象
此时是有的。因为上面已经放入了。
singletonObjects就是容器池(一级缓存,里面放的都是bean)
isSingletonCurrentlyInCreation 表示是否正在创建,也就是set集合里是否包含此对象
earlySingletonObjects表示三级缓存
allowEarlyReference表示是否支持循环引用
singletonFactories就是二级缓存,也就是单例工厂。
这里面的3个缓存的作用:
一级缓存:专门存放springbean
二级缓存:实际上是单例工厂,为什么不是一个简单的map,而要用一个工厂。是因为spring要对对象还可能要进行改造,很经典的场景就是aop的应用。
如果只是放入map,就不能进行扩展了,只能是原封不动的返回。工厂模式的应用,工厂就是生产对象用的,但是在产生一个对象之前还可以做很多事。
三级缓存:其实这个缓存如果去掉,完全可以。它出现的目的是为了解决性能的问题(防止重复生产对象)。
比如:还有一个test3也引用了test1,根据上面的流程,还要从单例工厂里获取(二级缓存),因为是单例,每次产生的对象都一样,之前已经生产过了,而且放入了三级缓存。
所以直接去三级缓存去取就行了,不用再工厂里再产生一个了。
当然,循环引用也可以关闭
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(); ac.setAllowCircularReferences(true); ac.register(AppConfig.class); ac.refresh();
AppConfig类就是要扫描业务类