spring源码分析——三级缓存与循环依赖的实现

  在使用spring框架开发时,会出现类A 依赖  类B  ,类B 又依赖 类A的情况,就是循环依赖了,那么spring容器是怎么处理的呢

在看循环依赖之前我们先来看一下spring中的三级缓存。

一:spring容器中的缓存

spring容器对对象的注册维护,主要是通过DefaultSingletonBeanRegistry来实现的,这个类提供了一些存储以及获取的方法,我们首先类分析一下这个类

spring源码分析——三级缓存与循环依赖的实现

下面是三级缓存集合:

spring源码分析——三级缓存与循环依赖的实现

下面看一下这个类里基本的方法:

注册单例bean:

spring源码分析——三级缓存与循环依赖的实现

放入一级缓存中:

spring源码分析——三级缓存与循环依赖的实现

 放入三级缓存中:

spring源码分析——三级缓存与循环依赖的实现

获取单例对象的过程:

spring源码分析——三级缓存与循环依赖的实现

获取单例bean的核心逻辑:

spring源码分析——三级缓存与循环依赖的实现

 spring源码分析——三级缓存与循环依赖的实现

二: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看起

spring源码分析——三级缓存与循环依赖的实现

如果缓存中不存在,那么就调用getSingleton实例化TestA对象:

spring源码分析——三级缓存与循环依赖的实现

spring源码分析——三级缓存与循环依赖的实现

在堆内存中开辟空间,创建对象后,如果单例bean,运行循环依赖,那么放入三级缓存中。

spring源码分析——三级缓存与循环依赖的实现

设置属性:

spring源码分析——三级缓存与循环依赖的实现

设置属性testB

spring源码分析——三级缓存与循环依赖的实现

具体Autowired的过程这里不再分析,可以看上面章节分析Autowired注入的过程,在AutowiredAnnotationBeanPostProcessor方法中会涉及到getBean(testB),然后又会触发

testB实例化的过程,

spring源码分析——三级缓存与循环依赖的实现

递归回到最开始testA的实例化过程,流程和之前实例化testA一样,最后也是走到设置属性:

spring源码分析——三级缓存与循环依赖的实现

testB实例化过程中,设置属性testA

spring源码分析——三级缓存与循环依赖的实现

又会走到getBean实例化testA,这时又递归回到了DefaultListableBeanFactory的getBean方法

spring源码分析——三级缓存与循环依赖的实现

spring源码分析——三级缓存与循环依赖的实现

spring源码分析——三级缓存与循环依赖的实现

然后返回一个没有初始化的testA对象,然后testB设置属性成功,初始化完成,缓存并返回对象,

又回到了testA对象设置属性的时候,然后testA设置属性完成,到这里单例的循环依赖就成功的解决了。

测试代码:

spring源码分析——三级缓存与循环依赖的实现

 运行结果:

spring源码分析——三级缓存与循环依赖的实现

 通过结果可以看出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;
	}

}

运行一下:

spring源码分析——三级缓存与循环依赖的实现

spring源码分析——三级缓存与循环依赖的实现

抛出异常:从运行结果可以看出,首先实例化testA,创建testA实例的时候,发现是有参数构造函数,所以寻找参数testB,然后getBean,实例化testB,

发现testB也是有参数构造函数,所以寻找参数testA,getBean(testA),由于testA还没有创建,所以没法放入集合中标记,抛出异常。

总结:spring不支持构造器的循环依赖

3:prototype类型非单例循环依赖

doGetBean方法中:

spring源码分析——三级缓存与循环依赖的实现

准备工作:

@Component
@Scope("prototype")
public class TestA {

	@Autowired
	private TestB testB;

}

  

@Component
@Scope("prototype")
public class TestB {

	@Autowired
	private TestA testA;

}

  

运行测试用例:

spring源码分析——三级缓存与循环依赖的实现

从运行结果可以看出,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源码分析——三级缓存与循环依赖的实现

正常运行,没有抛出异常。

因为spring预实例化只会实例化非单例的bean,那么TestB会先实例化,设置属性时发现依赖TestA ,然后实例化TestA,

TestA是非单例,创建bean,设置属性时,发现依赖TestB,getBean(testB),实例化TestB,这时缓存中已经存在testB,从

三级缓存中拿到testB,TestA实例化完成,返回对象,TestB实例化完成。