在使用spring框架开发时,会出现类A 依赖 类B ,类B 又依赖 类A的情况,就是循环依赖了,那么spring容器是怎么处理的呢
在看循环依赖之前我们先来看一下spring中的三级缓存。
一:spring容器中的缓存
spring容器对对象的注册维护,主要是通过DefaultSingletonBeanRegistry来实现的,这个类提供了一些存储以及获取的方法,我们首先类分析一下这个类
下面是三级缓存集合:
下面看一下这个类里基本的方法:
注册单例bean:
放入一级缓存中:
放入三级缓存中:
获取单例对象的过程:
获取单例bean的核心逻辑:
二:spring循环依赖的源码分析
1:单例bean属性的循环依赖
准备工作:创建TestA类,依赖TestB类,然后TestB类 又反过来依赖 TestA 类
@Component public class TestA { @Autowired private TestB testB; }
@Component public class TestB { @Autowired private TestA testA; }
我们来分析一下这种属性循环依赖的情况
我们从AbstractBeanFactory类的doGetBean看起
如果缓存中不存在,那么就调用getSingleton实例化TestA对象:
在堆内存中开辟空间,创建对象后,如果单例bean,运行循环依赖,那么放入三级缓存中。
设置属性:
设置属性testB
具体Autowired的过程这里不再分析,可以看上面章节分析Autowired注入的过程,在AutowiredAnnotationBeanPostProcessor方法中会涉及到getBean(testB),然后又会触发
testB实例化的过程,
递归回到最开始testA的实例化过程,流程和之前实例化testA一样,最后也是走到设置属性:
testB实例化过程中,设置属性testA
又会走到getBean实例化testA,这时又递归回到了DefaultListableBeanFactory的getBean方法
然后返回一个没有初始化的testA对象,然后testB设置属性成功,初始化完成,缓存并返回对象,
又回到了testA对象设置属性的时候,然后testA设置属性完成,到这里单例的循环依赖就成功的解决了。
测试代码:
运行结果:
通过结果可以看出spring是支持单例的属性的循环依赖的
2:单例bean 构造器的循环依赖
准备工作:
@Component public class TestA { private TestB testB; @Autowired public TestA(TestB testB){ this.testB = testB; } }
@Component public class TestB { private TestA testA; @Autowired public TestB(TestA testA){ this.testA = testA; } }
运行一下:
抛出异常:从运行结果可以看出,首先实例化testA,创建testA实例的时候,发现是有参数构造函数,所以寻找参数testB,然后getBean,实例化testB,
发现testB也是有参数构造函数,所以寻找参数testA,getBean(testA),由于testA还没有创建,所以没法放入集合中标记,抛出异常。
总结:spring不支持构造器的循环依赖
3:prototype类型非单例循环依赖
doGetBean方法中:
准备工作:
@Component @Scope("prototype") public class TestA { @Autowired private TestB testB; }
@Component @Scope("prototype") public class TestB { @Autowired private TestA testA; }
运行测试用例:
从运行结果可以看出,spring容器不支持prototype类型的循环依赖。
4:如果相互依赖的两个类,一个类TestA是非单例,另一个类TestB单例,会怎样?
@Component @Scope("prototype") public class TestA { @Autowired private TestB testB; }
@Component //@Scope("prototype") public class TestB { @Autowired private TestA testA; }
运行结果:
正常运行,没有抛出异常。
因为spring预实例化只会实例化非单例的bean,那么TestB会先实例化,设置属性时发现依赖TestA ,然后实例化TestA,
TestA是非单例,创建bean,设置属性时,发现依赖TestB,getBean(testB),实例化TestB,这时缓存中已经存在testB,从
三级缓存中拿到testB,TestA实例化完成,返回对象,TestB实例化完成。