1.Spring是什么?

Spring有很庞大的家族,Spring一般指的其实就是SpringFramework! Ioc和aop

包含在SpringFramework中!

SpringFramework介绍和特点:

可以看到SpringFramework包含了我们通常使用的ioc,aop

有兴趣的童鞋可以把源码下载下来研究,下载下来的源码可以修改

Springframework源码地址:https://github.com/spring-projects/spring-framework

也可以本地项目引入依赖进行研究:

<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.0.RELEASE</version>
</dependency>

2.Spring Demo演示

引入依赖,创建项目:

创建X,Y类,加上@Component注解

配置spring要扫描的包

创建Main方法,使用AnnotationConfigApplicationContext  加载配置类

运行完上图中第17行代码AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);Spring容器就会被初始化,spring容器就会去加载config配置类,解析config类(由于config加了@ComponentScan注解)就会扫描com.spring包下的类,就会把加了注解@component的类加载到spring容器并实例化,所以就能X类打印出来

3.什么是循环依赖

循环依赖:

X中依赖Y,Y中依赖X就叫循环依赖,只有单例才支持循环依赖,并且不支持构造方法依赖

4.为什么探讨循环依赖

为什么探讨循环依赖?

因为spring bean不等同于java对象,java对象依赖可以使用set(new Object)完成循环依赖。Spring bean不能像java对象一样完成这样的依赖

普通java对象实例过程:new 的过程

Java虚拟机启动,类加载器把编译好的class文件信息加载到方法区

Spring bean实例过程

加了注解的类,spring 容器会把该类的class文件加载成一个BeanDefinition对象(存放了该类的所有信息),比如类名就放在 factoryBeanName中

然后把该BeanDefinition放入一个map中。Spring 就会遍历这个map,把所有的BeanDefinition拿出来做验证,验证BeanDefinition对应的类要不要实例化,因为有的类不要实例化(@Scope(“prototype”)),懒加载(@Lazy(true))的也不会马上实例化。实例化以后的bean放入一个单例池中(一个map中)。

伪代码:

扩展:可以使用BeanDefinitionMap偷天换日

5.源码实现关键过程

先了解Bean生命周期:

Bean生命周期:
1.scan  ---变成beandefinition  放进map
2.遍历map 得到beanDefinition
3.验证bd是否要实例化
4.通过bd,getClass得到class
5.推断构造方法(因为class里可能有很多构造方法)
6.实例化对象(这时候只是一个对象,不是一个完整的bean)
7.合并bean
8.缓存一个工厂
9.填充bean (判断bean有多少属性),自动注入
10.执行一部分Aware,比如ApplicationContextAware
11.执行生命周期初始化回调方法 (有三种方法:1.打上@PostConstruct注解,2.实现InitializingBean接口,3,xml配置)@PostConstruct   //生命周期回调方法 ,注解版public void init(){System.out.println("annotation init");}@Override   //生命周期回调方法,接口板public void afterPropertiesSet() throws Exception {System.out.println("implements inti");}
12.接口生命周期回调方法,执行一部分aware
13.如果有aop  执行aop代理

开始源码分析:

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);执行这段代码的时候,点进去会发现首先通过this()会调用父类的一个方法

xy实例完成:

继续跟踪this.refresh()

invokeBeanFactoryPostProcessors方法完成了扫描,变成BeanDefinition,并完成实例

上图其中的五个变量是spring最重要的五个类,开天辟地,比如第一个类,主要完成注解扫描

探讨循环依赖

1.spring开始实例化单例的类

继续跟踪这个方法

继续跟踪这个方法:拿到所有的BeanNames,并遍历

通过beanName从BeanDefinationMap中得到BeanDefinition

开始验证:

继续跟踪getBean方法:

跟踪一getSingleton方法

所有类实例化以后都在图中的单例池map中,下图中框起来的是就是spring 单例池(是一个map)

证明一下单例池是一个Hash

继续回到getSingleton()判断是否从单例池中拿到beanName对应的bean

因为第一次实例,从单例池中拿不到x。所以走的是上面的else ,往下会发现继续验证是否有@DependsOn注解等:

上图中,检查是单例,使用lambam调用createBean创建bean

doCreateBean方法中:

跟踪addSingletonFactory方法

回到doCreateBean方法看:此时,X只是一个对象,还不是bean

判断是否支持循环依赖,是的话提前暴露一个工厂进单例工厂map中

继续往下看,看addSingletonFactory方法:

继续往下:调用populateBean方法,完成属性填充,此时生命周期,Aware还没执行,所以还不是完整的bean

继续,该执行一部分aware了

执行aware的原理,把刚刚实例好的bean传进去进行判断,然后直接执行

继续研究:执行applyBeanPostProcessorsBeforeInitialization后,aware和部分生命周期执行完成,但是还有接口板的aware没有执行。

紧接着就是接口板的aware执行。

代理aop

在回顾问题:为什么探讨循环依赖?

因为x实例的过程中要去实例y,这时候x还没完成。然后去实例y的时候又发现要x,发现x也没有实例完成,所以又去实例x,x又去实例y,最终造成死循环,所以要探讨循环依赖!

回顾:

Spring 容器初始化过程

其中在finishBeanFactoryInitialization(beanFactory);方法中完成bean的实例化(需要validate校验和执行life周期函数)

进入finishBeanFactoryInitialization(beanFactory);继续追踪实例化的方法。

preInstantiateSingletons中进行一系列的校验

getBean调用doGetBean,先中容器中的单例池中尝试获取,获取到则返回,没获取到继续验证,然后调用createBean

上图createBean中,通过bd,拿到class,反射调用构造方法实例对象

对象实例好了,回到doCreateBean方法。

继续往下:判断是否支持循环依赖,如果支持,就往singletonFactories(Map)中缓存一个工厂,传入bean的名字,ObjectFactory

然后执行一部分aware,判断有没有aop,有就代理上,最后把bean put进单例池中

从容器中获取对象

背后的过程:

判断类是否有别名,有的话获取所有别名

一路追踪,最终还是去从单例池中获取!!!

思考x依赖y,y依赖x的过程:

当X类中有Y,Y类中有X时,实例X的时候,走到populateBean需要填充Y,肯定会调用getBean()去singleObjects(单例池)中拿Y

论证:实例x的时候

接下来又执行了一遍:这次beanName = y

发现y在单例池中没有,调用createBean()创建y

创建y的时候发现要去填充x,所以又去创建x,创建x的过程首先先去单例池中拿,由于x正在创建,所以单例池中没有,拿不到!

解决死循环创建的第一步:

1.跟踪关键方法:getSingleton

看一下beforeSingletonCreation里面做了啥:判断当前创建的beanName是否包含在正在创建的集合中,如果没有,则把beanName放进正在创建的集合中

回到填充熟悉阶段:x填充y,发现y没有,然后就去创建y,当y填充x的时候,去拿一遍x

拿x的代码过程:先去单例池中拿,没有拿到,判断是否正在被创建(true,因为第一次实例的时候会把正在创建的beanName 放进一个map中),如果是,就去三级缓存earlySingletonObjects拿,没有拿到就获取x的beanFactory(之前已经暴露,所以可以拿到)

拿到X的singletonFactory:

然后通过singletonFactory拿到x Object(没有实例完成的),把x Object(singletonObject)放进三级缓存earlySingletonObjects,看图:

这样,y就能把x填充好了(x 还是Object,还不是bean),然后返回的x填充y的那段代码继续执行,循环依赖就解决了!

思考:三个缓存的作用?两个缓存不就够了吗?为啥要三级缓存!

两个确实够了,但是做了一个三级缓存能提高效率:因为:beanFactory的创建需要一个过程,如果首先去一个三级缓存里面能拿到,就直接完事了,如果没有,再去判断BeanFactory里面有没有,没有再去创建。

贴一个学习笔记图:

最后回顾一下 bean实例的生命周期:

Bean生命周期:
1.scan  ---变成beandefinition  放进map
2.遍历map 得到beanDefinition
3.验证bd是否要实例化
4.通过bd,getClass得到class
5.推断构造方法(因为class里可能有很多构造方法)
6.实例化对象(这时候只是一个对象,不是一个完整的bean)
7.合并bean
8.缓存一个工厂
9.填充bean (判断bean有多少属性),自动注入
10.执行一部分Aware,比如ApplicationContextAware
11.执行生命周期初始化回调方法 (有三种方法:1.打上@PostConstruct注解,2.实现InitializingBean接口,3,xml配置)@PostConstruct   //生命周期回调方法 ,注解版public void init(){System.out.println("annotation init");}@Override   //生命周期回调方法,接口板public void afterPropertiesSet() throws Exception {System.out.println("implements inti");}
12.接口生命周期回调方法,执行一部分aware
13.如果有aop  执行aop代理

很重要的beanDefiniton:

Bean的建模对象:BeanDefinition(包含了bean的类型,作用域,是否懒加载,注入模型,构造方法参数,set方法参数等信息)

Spring Bean实例化过程,怎么解决循环依赖相关推荐

  1. Spring 源码解析 - Bean创建过程 以及 解决循环依赖

    一.Spring Bean创建过程以及循环依赖 上篇文章对 Spring Bean资源的加载注册过程进行了源码梳理和解析,我们可以得到结论,资源文件中的 bean 定义信息,被组装成了 BeanDef ...

  2. 京东一面:Spring 为何需要三级缓存解决循环依赖,而不是二级缓存?我懵了。。...

    欢迎关注方志朋的博客,回复"666"获面试宝典 来源:cnblogs.com/semi-sub/p/13548479.html 前言 bean生命周期 三级缓存解决循环依赖 总结 ...

  3. spring无法用三级缓存解决循环依赖的问题分析

    spring无法解决构造器的循环依赖,对上述例子稍微进行改动: @Component("b") public class B {private A a;public B(A a) ...

  4. 从源码了解spring bean实例化过程

    我们先来看下spring如何手动初始化一个对象 ClassPathResource res = new ClassPathResource("beans.xml"); Defaul ...

  5. 《Spring源码深度解析 郝佳 第2版》bean的加载、循环依赖的解决

    往期博客: <Spring源码深度解析 郝佳 第2版>容器的基本实现与XML文件的加载 <Spring源码深度解析 郝佳 第2版>XML标签的解析 往期博客完成了xml文件加载 ...

  6. Spring IOC 容器源码分析 - 循环依赖的解决办法

    1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...

  7. 为什么Spring需要三级缓存解决循环依赖,而不是二级缓存?

    来源:https://www.cnblogs.com/semi-sub/p/13548479.html 在使用spring框架的日常开发中,bean之间的循环依赖太频繁了,spring已经帮我们去解决 ...

  8. Spring解决循环依赖

    跳出源码地狱,Spring巧用三级缓存解决循环依赖-原理篇-Java知音 概述 使用两级缓存即可解决循环依赖问题,但是Spring还需要支持AOP,对象被AOP之后会生成一个新的对象,如果在将实例注入 ...

  9. 【171期】面试官:小伙汁,Spring是怎么解决循环依赖的呢?

    程序员的成长之路 互联网/程序员/技术/资料共享 关注 阅读本文大概需要 17 分钟. 来自:blog.csdn.net/Baisitao_/article/details/107349302 前言 ...

最新文章

  1. java map随机取值_HashMap随机取值和迭代器取值的对比
  2. php中单引号和双引号的区别,哪个速度更快?为什么?
  3. 一颗强健的“心脏”,让海银的业务系统更高效、更安全!
  4. 强化学习note1——马尔科夫奖励过程MRP和马尔科夫决策过程MDP各个函数的定义与区别
  5. 2018年春阅读计划---阅读笔记4
  6. 产品设计的Kawaiization
  7. Guid.NewGuid().ToString()的几种格式 (转)
  8. GNS3连接VMware中虚拟主机,能相互ping通
  9. 用于保存计算机输入输出数据的材料及其,与房地产,电子,金融,汽车并称五大产业的是()...
  10. JS获取键盘码并判断按键
  11. 现代控制理论4——线性系统状态方程的解
  12. python输出日历_python输出指定月份日历的方法
  13. 1.checkpoint防火墙安装以及高可靠性配置
  14. 微信小程序怎么登录?如何正确登录微信小程序后台?
  15. 编程过程分享1「欢迎萌新入坑」:Python:做一个上海计算机二级答题系统的过程
  16. Ubuntu16.04如何调整屏幕分辨率至1920*1080
  17. 【机房收费个人版】DataTable 与泛型集合的较量
  18. 2020起重机械指挥证考试及起重机械指挥模拟考试题库
  19. 查询一年1、1-2月、1-3~一直到1-12月
  20. 新版Zotero插件更新

热门文章

  1. S32编译器问题汇总
  2. C51 以PWM实现呼吸灯
  3. ArcEngine编辑Feature
  4. 记. ZIP炸弹防御问题
  5. 托管IDC 机房的的几点优势
  6. 【收藏】HUE配置HDFS报错Cannot access: /. The HDFS REST service is not available. “ ““
  7. android studio 7200u,#本站首晒# 多图杀猫 华为MateBook X上手体验
  8. Xcode8配置支持10.1的系统
  9. Python+Excel+VBA实现批量自助生成名牌
  10. 强化学习(一)-->隐马尔科夫模型HMM-->HMM模型基础