spring循环引用

spring源码(一)spring循环引用

spring源码(一)spring循环引用

spring在默认单例的情况下是支持循环引用的

1.png

2.png

上图这两个类相互引用了对方(循环依赖)spring在单例、非构造方法注入的情况下是允许这样的循环依赖 上面的代码可以正常输出 从容器中可以获取到Xbean Ybean

Spring的循环依赖可以关闭

1、spring提供的APi

2、修改源码

循环依赖无非就是属性注入

spring的属性注入属于Spring bean的生命周期的一部分

两个相关概念:

1、spring bean——受spring容器管理的对象,可能经过了完整的spring生命周期(为什么是可能?难道还有bean是没有经过bean生命周期的?答案是有的),最终存在spring容器当中;一个bean一定是个对象。

2、对象——任何符合java语法规则实例化出来的对象,一个对象并不一定是spring bean 所谓bean的生命周期就是磁盘上的类通过扫描,然后实例化,跟着初始化,继而放到容器当中的过程。

spring bean的生命周期经历步骤

3.png

1:实例化一个ApplicationContext的对象;

2:调用bean工厂后置处理器完成扫描;

3:循环解析扫描出来的类信息;

4:实例化一个BeanDefinition对象来存储解析出来的信息;

5:把实例化好的beanDefinition对象put到beanDefinitionMap当中缓存起来,以便后面实例化bean;

6:再次调用bean工厂后置处理器;

7:当然spring还会干很多事情,比如国际化,比如注册BeanPostProcessor等等,如果我们只关心如何实例化一个bean的话那么这一步就是spring调用finishBeanFactoryInitialization方法来实例化单例的bean,实例化之前spring要做验证,需要遍历所有扫描出来的类,依次判断这个bean是否Lazy,是否prototype,是否abstract等等;

8:如果验证完成spring在实例化一个bean之前需要推断构造方法,因为spring实例化对象是通过构造方法反射,故而需要知道用哪个构造方法;

9:推断完构造方法之后spring调用构造方法反射实例化一个对象;注意这里说的是对象、对象、对象;这个时候对象已经实例化出来了,但是并不是一个完整的bean,最简单的体现是这个时候实例化出来的对象属性是没有注入,所以不是一个完整的bean;

10:spring处理合并后的beanDefinition(合并?是spring当中非常重要的一块内容,后面会分析);

11:判断是否支持循环依赖,如果支持则提前把一个工厂存入singletonFactories——map;

12:判断是否需要完成属性注入

13:如果需要完成属性注入,则开始注入属性

14:判断bean的类型回调Aware接口

15:调用生命周期回调方法

16:如果需要代理则完成代理

17:put到单例池——bean完成——存在spring容器当中

spring大概在什么时候实例化bean的

spring在AbstractApplicationContext#finishBeanFactoryInitialization方法中完成了bean的实例化。这点需要记住

4.png

5.png

spring bean实例化的过程理论:

假设磁盘上有X、Y类,spring启动时会扫描到X、Y被加了注解(@Autowired)然后会把X、Y的class信息封装成为一个beanDefinition对象,这个beanDefinition对象包含了当前类的信息,比如当前类是不是抽象的、当前这个类的描述、当前这个类的类型、当前这个类是不是懒加载等等这些信息。封装完之后会把beanDefinition对象放到一个Map集合当中,如果有的话执行beanFactoryPostProcessor(扩展)。然后开始验证,如果验证都通过的话调用prelnstantiateSingletons方法(过程相当复杂) 开始实例化当前类,实例化之后成为一个spring bean放到单例池里面。

源码验证:

6.png

7png.png

这段代码用来初始化spring容器,当这段代码执行完之后,XY已经被实例化好了

debug源码找到在哪里实例化好的bean

8.png

beanFactory->beanDefinitionMap中还不存在x、y类

9.png

10.png

扫描具体的实现代码

11.png

put到beanDefinitionMap

12.png

调用太多了

13.png

看源码中在AnnotationConfigApplicationContext类的父类定义了beanFactory

14.png

这个beanFactory就是我们常说的spring bean工厂 它是DefaultListableBeanFactory的实例

15.png

在DefaultListableBeanFactory中有一个属性叫做 beanDefinitionMap ConcurrentHashMap 1.7、1.8 解决并发

17.png

执行完invokeBeanFactoryPostProcessors方法发现beanDefinitionMap中有了x、y

所以nvokeBeanFactoryPostProcessors中完成了扫描把类变成了beanDefinition对象(注意这个时候spring并没有对bean进行实例化)

18.png

而是在finishBeanFactoryInitialization实例化单例的bean

19.png

20.png

21.png

BeanFactoryPostProcessors:

bean工厂后置处理器 能够干扰spring当中bean工厂初始化过程?

扩展:可以在初始化前后做一些事情 实现这个接口可以拿到 beanFactory可以对beanFactory做一些改变等

22.png

重点调用:finishBeanFactoryInitialization方法中beanFactory.preInstantiateSingletons();

23.png

拿到beanNames集合 为了遍历

24.png

为什么要有 beanDefinition:

用来描述spring bean的里面包含了spring bean的所有信息(因为普通的类 不能描述 scope lazy等等等等 ) 调用到doGetBean ac.getBean其实底层就是调用这个doGetBean方法

25.png

26.png

singletonObjects:单例池

27.png

28.png

创建bean之前的关键代码

29.png

singletonsCurrentlyInCreation 正在创建的单例bean的名字集合 放在这个集合当中

30.png

关键代码 调用传过来的 ObjectFactory<?> singletonFactory的方法 lambda

31.png

再往下执行调到这里

32.png

到此bean 还没有被创建

33.png

创建对象

34.png

35.png

36.png

依次进入方法内部 37.png

38.png

39.png

40.png

41.png

这个时候只是实例化出来了对象还不是一个bean

42.png

判断支持循环依赖 allowCircularReferences默认等于true

43.png

44.png

spring处理循环依赖时候涉及的三个Map

45.png

46.png

执行完这个方法后注入的属性有值了

47.png

接下来调用initializeBean这个方法

48.png

49.png

//执行部分Aware接口

50.png

生命周期方法的回调

51.png

52.png

53.png

54.png

生命周期回调初始化方法有三种实现方式 可以同时存在一个bean当中

因为在源码中执行时机是不一样的

55.png

56.png

spring bean的生命周期 调用过程

57.png

会按照字母顺序创建(可以更改)

循环依赖!

58.png

x属性注入 填充y 调到了doGetBean(“y”) 肯定拿不到

59.png

60.png

正在创建x的过程当中创建y所以singletonsCurrentlyInCreation有x

61.png

X和Y正在被创建的过程当中

62.png

执行这个方法之前y还没有别创建 一旦执行完 控制台打印y

63.png

64.png

这个时候y要注入x所以回去getBean(“x”)

65.png

66.png

关键代码: 这个时候返回的肯定不能是null如果返回null的话就成了死循环了 又会去走创建x的步骤

67.png

(从单例池当中去拿x但是 x也肯定不在单例池当中因为它没有走完bean的生命周期)

68.png

getSingleton源码 注意第二个参数写死了true

69.png

单例池——一级缓存主要存放单例bean (所谓的容器)

现在去从单例池当中去拿x 拿不到 返回 null

70.png

71.png

这里判断x是不是正在被创建 true

72.png

这里还是拿不到 原因很简单,因为前面根本就没有网这个map三级缓存中放入

73.png

74.png

这里肯定成立

75.png

这个地方可以拿到 但是这个不是x类型 它是Factory

76.png

77.png

问题: 1、工厂怎么来的?

2、为什么不直接在二级缓存中存一个x,而要存一个工厂?

3、x和工厂怎么联系起来?

4、工厂造出来的x和正在创建的x是同一个吗 ? 是同一个

5、如果不是循环依赖的bean会不会执行三级缓存 ? 答: 不会

回答1:与bean的生命周期有关,下图

78.png

回答2:

因为spring如果直接把x存进二级缓存,那么在y注入x的时候,需要的x不是当前存进去的x

解释:存工厂可以产生任何对象,比存一个对象更加丰富 可以对x进行加工

回答三:

先把x传进去,然后增强,返回出来

79.png

回答四: 默认是同一个

80.png

81.png

为什么要添加到三级换存当中 :把它放到三级缓存 (x依赖y,y依赖x,x依赖z,z依赖x的情况下,x就不需要重复从工厂产生了),说白了为了效率

为什么要从二级缓存remove掉:为了gc 资源回收

singletonsCurrentlyInCreation 这个集合80%是为了循环依赖而使用的

beanDefinition A类去掉注解

82.png

然后通过ac来获取a 肯定会报错 因为A不在spring容器当中

83.png

84.png

85.png

调用了程序员或者spring当中提供的所有的postProcessors

//拿到spring当中所有的BeanFactoryPostProcessor对象 执行postProcessBeanDefinitionRegistry方法 完成的扫描,扫描之后就变成了beanDefinition

86.png

所谓的扫描就是执行spring内部的BeanFactoryPostProcessor的子类当中的postProcessBeanDefinitionRegistry方法完成的扫描

87.png

88.png

89.png

怎么区分是spring内部和程序员自己提供的?

找出所有实现了接口的子类,并且找了两次,代码几乎一摸一样 第一次找spring自己内部的 第二次找程序员写的

90.png

spring 扩展点

91.png

92.png

比如:把class给改了 改成了A,现在就可以拿到A了 而B就拿不到了

写在后面:

我是YC一位普通的从事软件开发行业的工作者,工作了很多年第一次写的一篇技术博客。这也是我学习到的东西,做一个分享吧!再此感谢子路老师,他的源码性的东西讲的非常的深入和细致。我也是听了他的课之后做一个总结,再次感谢!