Spring FrameWork从入门到NB -三级缓存解决循环依赖内幕 (一)

这篇具有很好参考价值的文章主要介绍了Spring FrameWork从入门到NB -三级缓存解决循环依赖内幕 (一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

循环依赖就是我依赖你、你依赖我,或者A依赖B、B依赖C、C依赖A…组成的错综复杂的依赖关系。

其实各种不同的依赖关系最终从逻辑上都可以演变为:我依赖你、你依赖我。

循环依赖大致可以分为两种情况:

  1. 属性依赖:比如A对象有一个属性B,B对象有属性A。
  2. 构造器依赖:A对象的构造器中有参数B,B对象的构造器有参数A。

构造器依赖是Spring解决不了的(指的是A、B不存在其他构造器,或者Spring需要通过互相依赖的构造器创建A、B对象),这种死结你自己写代码都没办法解决,Spring当然也没有办法。

Spring通过其特有的三级缓存机制能解决属性依赖。顺便说一下,其实Spring官网或相关资料中并没有提出过三级缓存的说法,也不太清楚这种说法从哪儿来(或者是我疏漏了没有查到),反正很NB、很高深的样子,其实如果你认真读源码,换做一种真实的说法:Spring最终通过三个Map的巧妙使用解决循环依赖问题…也就没有那么NB、那么高深、尤其是没有那么高大上了。但是不管怎么说,Spring解决循环依赖的框架还是值得我们认真学习掌握的。因为Spring各路大牛的设计思想确实#@¥%……%*(确实和你的收入会直接相关所以你一定有必要花点时间精力彻底搞清楚)。

三级缓存框架

先上一张图,对三级缓存有个直观印象:
Spring FrameWork从入门到NB -三级缓存解决循环依赖内幕 (一)

抱歉,画的有点low,不过还是能说明问题的哈。

Spring IoC容器中当然不止是DefaultSingletonBeanRegistry一个容器,但三级缓存就是针对单例Bena来说的,其实如果只是从Bean容器的角度来讲,Spring IoC容器很大程度上讲的也就是单例Bean的容器,原型Bean本来也是每次使用、每次创建,所以也就不需要缓存。只有单例Bean在Spring IoC容器中是一次创建长期驻留,驻留的地方就是三级缓存中的“一级缓存”。

如上图,三级缓存定义在DefaultSingletonBeanRegistry中:

  1. 三级缓存:存储bean name和bean工厂的容器。
  2. 二级缓存:存储bean name和bean实例,二级缓存中的bean实例刚刚完成实例化、尚未完成属性赋值,所以是半成品。
  3. 一级缓存:存储最终完成创建的Bean,应用getBean或者自动装配的Bean就是从以及缓存获取的。
Spring解决循环依赖的详细过程

其实认真读源码,研究完Spring的Bean创建过程之后,循环依赖的原理就一目了然了(虽然很复杂)。

Spring的Bean创建过程相对来说还是比较复杂的,研究清楚全貌还是要花费点时间的,我们的策略就是一步一步抽丝剥茧以及各个击破,今天的主要目标就是三级缓存解决循环依赖的过程,所以对其他过程就要忽略。

Spring源码,Bean的创建过程:

AbstractBeanFactory#getBean

创建和获取bean的统一入口,会调用doGetBean方法。

AbstractBeanFactory#doGetBean
  1. 首先调用DefaultSingletonBeanRegistry#getSingleton(beanName,true)方法,注意这个getSingleton方法是不会创建bean的。
  2. 如果没有拿到Bean,就准备创建Bean
  3. 首先检查是否有Dependon,有的话,先创建Dependon
  4. 如果要创建的是单例bean,则调用getSingleton(beanName,factory)创建bean,注意这个才会创建bean
  5. 否则,如果是原型bean…和今天的话题无关,暂时忽略
DefaultSingletonBeanRegistry#getSingleton(beanName,boolean)

我们上面说过了,这里不会创建bean:

  1. 一级缓存存在,则直接返回
  2. 一级缓存不存在并且Bean正在创建中,从二级缓存获取
  3. 二级缓存获取到则返回,否则检查三级缓存
  4. 三级缓存有的话,调用三级缓存的bean工厂加工bean,放入二级缓存后,返回,并清除三级缓存
  5. 否则三级缓存没有的话返回null
DefaultSingletonBeanRegistry#getSingleton(beanName,Factory)

这个getSingleton担负着创建bean实例的任务,如果获取不到的话,会创建:

  1. 一级缓存存在,则直接返回
  2. 否则,当前bean放入“正在创建中”列表(singletonsCurrentlyInCreation)
  3. 使用Factory创建Bean实例:调用AbstractAutowireCapableBeanFactory#createBean方法
  4. 当前bean从“正在创建中”列表移出
  5. 完成创建的Bean从二级、三级缓存移出,放入一级缓存

我们需要注意的是,这个方法调用完成之后,一个完整的bean就被创建出来、被放入到一级缓存了。

但你要知道的是,这个方法不会那么容易完成的,玄机就在createBean方法中,如果有依赖的话还会递归调用AbstractBeanFactory#getBean方法的,逻辑又绕回去了…

AbstractAutowireCapableBeanFactory#createBean

真正创建Bean的地方:

  1. 创建Bean实例(原始类的实例,或者CGLIB代理对象实例)
  2. 如果是单例Bean并且当前Bean正在创建中,则调用addSingletonFactory:作用是创建一个工厂方法,添加到三级缓存。
  3. 调用populateBean方法,属性填充(可能会发生循环依赖啊…)

需要特别注意的是,三级缓存就是在这儿创建的,存储的内容是一个bean factory、使用lamda表达式创建,工厂方法是getEarlyBeanReference。

getEarlyBeanReference方法后面从三级缓存获取对象的时候会回调,作用是通过SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法最终调用到wrapIfNecessary方法,目的是判断bean如果需要AOP的话则生成他的代理对象。

三级缓存存储bean factory、而不是存储bean实例的目的就是为了解决依赖注入过程中的代理对象的处理的。

这部分内容确实有点绕,是需要动动脑子认真仔细思考一下才能搞明白的。

AbstractAutowireCapableBeanFactory#populateBean

属性填充,这个过程中处理自动封装,会有依赖注入。

调用AutowiredAnnotationBeanPostProcessor#postProcessProperties方法。

然后,AutowiredAnnotationBeanPostProcessor#postProcessProperties进行属性赋值。

然后,再调用DefaultListableBeanFactory#resolveFieldValue方法。

最后,老套路,DefaultListableBeanFactory#doResolveDependency方法。

方法doResolveDependency中首先解决候选注入对象的问题,这部分代码中包含依赖注入是查找对象(DL)的真相,这部分不是今天的主题,跳过。

DL查找到的肯定不是待注入的对象,而是待注入对象的beanName。

那猜想一下,拿到待注入的beanName之后,一定是要拿到bean对象之后再注入的,Spring会怎么处理呢?怎么根据beanName拿到bean对象呢?

其实不难猜到!

doResolveDependency方法会调用DependencyDescriptor#resolveCandidate方法,resolveCandidate方法调用beanFactory.getBean获取对象,其实最终调用的是AbstractBeanFactory的getBean方法。

揉一揉昏花的老眼,是否感觉到似曾相识呢?

没错,往上翻,就是我们开始分析循环依赖的入口处,又转回去了。

所以没错,Spring IoC在Bean实例化、依赖注入的过程,就是一个不断递归调用的过程。

小结

以上,就是Spring IoC的bean的实例化以及依赖注入、三级缓存解决循环依赖的主要逻辑了,整理一下、用几个例子推导一下,相信就能搞清楚这部分内容了。

你如果不相信的话我们下一篇文章就来推导一下。

上一篇 Spring FrameWork从入门到NB -classpath扫描和组件托管文章来源地址https://www.toymoban.com/news/detail-500531.html

到了这里,关于Spring FrameWork从入门到NB -三级缓存解决循环依赖内幕 (一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • spring 的循环依赖以及spring为什么要用三级缓存解决循环依赖

            bean的生命周期         这里简单过一下 class -无参构造 -普通对象 -依赖注入(对加了autowire等的属性赋值) -初始化前-初始化 -初始化后(aop) -放入单例池的map(一级缓存) -bean对象 这里提一点单例bean单例bean 其实就是用mapbeanName,Bean对象创建的,多例bean就不

    2024年02月15日
    浏览(57)
  • Spring使用三级缓存解决循环依赖?终于完全弄明白了

    文章阅读前推荐 推荐先去看看源码,源码很短,但是对于我们在脑子里构建一个完整思路很重要。看起来非常简单,只需要双击shift,全局查找文件:AbstractAutowireCapableBeanFactory,找到550行左右的doCreateBean方法,重点看一下580行到600行这20行代码就行,包含了三级缓存、属性注

    2024年03月25日
    浏览(70)
  • Spring 为什么要用三级缓存来解决循环依赖(AOP),二级缓存不行吗

    解决有代理对象的循环依赖不一定要三级缓存,用二级甚至一级也能解决,下面讨论下Spring为什么选择三级缓存这个方案。 Spring最开始是没有三级缓存的,后面版本因为引入了AOP,有了代理对象,又因为存在循环依赖,为了保证依赖注入过程注入的是代理对象,且不完全打破

    2024年04月26日
    浏览(39)
  • SpringBoot 三级缓存解决循环依赖源码分析

    在 SpringBoot 框架中,如果只存在两级缓存,那么当发生循环依赖的时候可能存在异常的对象创建流程如下图所示: 创建 A 的空白对象 a1 解析填充 A 对象的属性,发现依赖的 B 对象未创建,则触发 B 对象创建 创建 B 对象过程中,填充对象属性时发现依赖 A 对象,此时从缓存中

    2024年02月11日
    浏览(50)
  • springIoc依赖注入循环依赖三级缓存

    理论思想,原来的对象是由使用者来进行控制,有了spring之后,可以把整个对象交给spring来帮我们进行管理 依赖注入,把对应的属性的值注入到具体的对象中,@autowired,populateBean完成属性的注入 beanFactory,存储对象,使用map结构来存储,在spring中一般存在三级缓存,singleton

    2024年01月16日
    浏览(45)
  • Spring FrameWork从入门到NB - BeanPostProcessor

    Spring框架之所以NB,BeanPostProcessor功不可没。BeanPostProcessor通过生命周期回调实现对Bean的增强比如,其实Spring的AOP功能主要就是通过BeanPostProcessor实现的,以及,Spring重要的自动装配功能,也是通过BeanPostProcessor实现的。 BeanPostProcessor生效原理 BeanPostProcessor指的是Spring容器中所

    2024年02月12日
    浏览(41)
  • Spring FrameWork从入门到NB - Spring AOP - 概念

    什么是AOP AOP - Aspect Oriented Programming的缩写,面向切面编程。 面向切面编程AOP是面向对象程序设计OOP的一个补充或扩展,是为了解决OOP在一些共性问题上的不足应运而生的。比如记录日志、事务处理、性能分析等等与业务无关的需求,需要发生在很多类的很多方法上,而需求

    2024年02月13日
    浏览(45)
  • Spring FrameWork从入门到NB -基于注解配置 & @Autowired注解

    基于注解的配置 其实前面我们已经在基于注解配置了,这是由于个人比较喜欢基于注解配置。 Spring官方文档提过一个问题:基于注解配置要好于基于xml的配置吗?其实各有千秋,根据个人喜好吧。重要的是Spring可以支持基于注解配置,也可以支持基于xml配置文件配置,也可

    2024年02月10日
    浏览(55)
  • Spring解决循环依赖问题

    例如,就是A对象依赖了B对象,B对象依赖了A对象。(下面的代码属于 属性的循环依赖 ,也就是初始化阶段的循环依赖,区别与底下 构造器的循环依赖 ) 问题来了: A Bean创建 —— 依赖了 B 属性 ——  触发 B Bean创建 ——  B 依赖了 A 属性 ——  需要 A Bean(但A Bean还在创建

    2024年02月12日
    浏览(45)
  • Spring解决循环依赖

    目录 什么是spring循环依赖 什么情况下循环依赖可以被处理? spring 如何解决循环依赖 创建A这个Bean的流程 答疑 疑问:在给B注入的时候为什么要注入一个代理对象? 初始化的时候是对A对象本身进行初始化,而容器中以及注入到B中的都是代理对象,这样不会有问题吗? 三级

    2024年02月22日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包