Spring的初始化流程以及循环依赖原因和解决方法

Spring的初始化过程

容器先初始化 就是调用复合方法refresh bean的生命周期是在refresh方法里面的 这个refresh方法里面和bean生命周期关系最大的两个方法是 registerBeanPostProcessors(beanFactory);
finishBeanFactoryInitialization(beanFactory) :这个方法将会对非延迟加载的单例Bean进行预实例化
他们都调用的getBean->doGetBean

bean的生命周期:spring先将打了@service这种注解的bean解析成BeanDefinition,并以beanName为key BeanDefinition为value存在concurrentHashMap里面 并且把bean再存在一个list里面 遍历这个list进行bean的实例化

bean的实例化就是先调用构造方法 然后通过beanwrapper进行依赖注入 然后看看有没有aware接口init-method什么的 也就是bean的生命周期的前面的几个过程
实例化完了就放在一级缓存里面

这个依赖注入是在getBean方法里面完成的。这个getBean实际上是AbstractBeanFactory的getBean

getBean(1.创建一个新的bean 2.从缓存里面拿bean)—>doGetBean–>getSingleton【这个方法就是尝试去三级缓存里面拿bean】–>getSingleton(a,()->{})【这是一个重载方法 作用就是创建bean创建完了就放进三级缓存 包括实例化/属性注入/初始化】-
在这里插入图片描述
上面这张图是创建bean的 也就是getSingleton的重载方法
如果不是延迟加载 ioc容器初始化的时候也会调用这个getBean 进行提前依赖注入 预实例化

如果延迟加载 那么就先初始化ioc 然后到了getBean这一步的时候 再来初始化bean

Bean的生命周期

1.实例化Bean:
IOC容器通过获取BeanDefination对象的信息进行实例化,实例化对象被包装在BeanWrapper(Bean的包装)对象中
2.设置对象的属性(DI依赖注入):getbean方法 --> createBean–>createBean里面的populateBean
真正执行依赖注入的就是populateBean()方法 就一行代码
但是这个方法最终调用的是beanwrapper接口的setPropertyValues方法 完成属性的注入
3.注入Aware接口():Spring会检测该对象是否实现了xxxAware()接口,并将相关的xxxAware实例注册给bean
【Aware接口就是增强bean的感知 接口里面啥也没有 但是BeanNameAware接口 继承Aware接口 然后有个setBeanName方法
如果bean实现了BeanNameAware接口,spring将bean的id传给setBeanName()方法;
如果bean实现了BeanFactoryAware接口,spring将调用setBeanFactory方法,将BeanFactory实例传进来】
4.BeanPostProcessor:自定义的处理(分为前置处理和后置处理)

5.InitializingBean 和 init-method:执行我们自己定义的初始化方法,也就是说是afterPropertiesSet()方法里面的内容 init-method是属性

6.使用
7.destory:bean的销毁 当然对应的肯定有DisposableBean接口和destroy-method属性

循环依赖

是什么?

就像controller依赖service service依赖dao一样
A依赖B B依赖C C依赖A
能解决的前置条件:
1.出现循环依赖的bean必须是单例
2.依赖注入的方式不能全是构造器注入
全是setter注入肯定没问题了能解决
setter+构造器注入也行好像还能搏一搏
如果是构造器注入呢 怎么办?
@lazy注解 让spring懒加载 当真正用到的时候 再去加载

对象实例化分为三步:
1.createBeanInstance():调用对象的构造方法实例化
2.populateBean() :属性填充
3.initializeBean() :调用spring xml中的init方法初始化

三级:singletonFactories : 早期曝光对象工厂
二级:earlySingletonObjects :早期曝光对象
一级:singletonObjects:单例池
怎么解决的?
通过三级缓存 我们创建对象分为三步 先调用构造函数实例化 然后属性填充 再初始化 允许调用构造函数之后 即使属性未填充 可以通过三级缓存提前暴露 就是把bean包装成一个工厂放进三级缓存 如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。
创建bean的时候Spring先从一级缓存中获取 获取不到就去二级缓存里面找 如果还找不到就去三级缓存通过singletionFactory.getObject()方法获取 如果获取到了就放进二级缓存里面

为什么要使用三级缓存呢?二级不可以吗?
如果用二级缓存 意味着Bean在实例化之后就完成AOP代理 但是Spring的设计原则是在Bean生命周期的最后一步完成AOP代理

所以spring解决循环依赖的关键就在于这个三级缓存 而加入三级缓存的前提是执行了构造器 所以纯构造器的循环依赖没办法解决
(可以通过@lazy注解延迟加载)

将bean包装成一个工厂 添加进了三级缓存

举例子:
比如A依赖了B B依赖了A A先进行初始化 刚完成第一步执行完构造方法 发现自己依赖B 就尝试去get(B) 但是这个时候B还没有呗创建出来 (这个时候已经暴露了)于是创建B B在初始化第一步的时候发现自己依赖A 然后去一级缓存里面找A 找不到 二级缓存 找不到 三级缓存 由于A将自己提前暴露了 所以B能拿到A 然后B就完成了初始化 把自己放进了一级缓存里面 然后A也完成了初始化

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

西安火车头

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值