• spring源码(3)


    循环依赖:X注入Y,Y注入X

    这对于普通对象来说,这很简单,先实例化X,Y,再把实例Y作为属性注入到X实例中就可以;而之所以对于bean对象单独拎出来讲,就是bean对象的创建是一个流程,一个生命周期,在这个生命周期结束前并不能停下来去实例化别的bean对象,也就是实例化X后不能直接实例化Y再将其作为属性注入,而不先实例化Y,又怎么将其作为属性注入呢?先画个流程图帮助理解我的问题:

    X注入Y:X实例化→X属性注入→实例化Y→Y属性注入→实例化X

    可以发现:在这个实例化X的生命周期中包含了实例化Y,可是实例化Y又得实例化X,这就形成了一个闭环,而spring是怎么解决这个问题的呢?

    这就要说起源码里的几处地方了:

     

     第一个:这个是doGetBean方法里的一开始就做的事,从单例池里拿bean,这个时候我们所拿的bean还在创建中,怎么可能从单例池里拿的到?那么这一行代码是不是多此一举?

    第二个:第二次在单线池取bean和第一次里的有什么区别?

    第三个:在实例化这一步,属性注入前我这里所说的暴露一个工厂指的是什么?

    也不卖关子,直接给说哈,第一个问题其实就是spring作者懒,还记得我们怎么通过容器取到已实例化好的bean对象吗?没错,调用的就是getBean方法,那其实就是调用doGetBean方法,所以这里其实是给想要调用已实例化好并放入单例池里用的,取得到直接返回,取不到,才进行我之前所说的一个bean对象的实例化过程;

    而第二和第三个问题其实就是回答了spring是怎么解决循环依赖的问题了,干讲作用啥的应该也难理解,我还是根据代码走完一个循环依赖的过程后,看看具体是怎样完成的,对应的答案应该也会随之出来:

    1、

     取不到

    2、

     我们打开这个getSinglenton方法到底做了什么:

     这个set名叫singletonsCurrentlyInCreations,用的也是ConcurrentHashMap创建的:

     

     我们可以发现,其实getSinglenton这个方法调用的传入来的参数,也就是createBean方法,我们打开看看:

    里面有这样一个方法:

     我们打开addSingletonFactory方法里做了什么:

     这里我们需要了解的是这个singletonFactory是什么,打开getEarlyBeanReference(beanName, mbd, bean));方法:

     这里的会扫描所有的内置接口和程序员所自定义的接口并执行里面的getEarlyBeanReference()方法,这也就对应了我之前说的程序员为什么实现某些特定的接口后就可以自定义功能,原因就在这;也就是,当有程序调用singletonFactory这一对象工厂里的getObject()方法时,就会执行getEarlyBeanReference(beanName, mbd, bean));方法,而我所说的暴露一个工厂,其实就是把这个工厂放在singletonFactories里;

    至此,bean对象开始依赖注入:

    在这一步前,我们先回顾有哪几个重要的集合类对象需要注意,做了什么:

    singletonsCurrentlyInCreations:存了一个Xbeanname;

    singletonFactories:存了一个key为Xbeanname,value为包含了Xbean的对象工厂

    在属性注入时,就会实例化我们Y,也就是Y会走一遍上面的流程,这样上面的集合就变为:

    singletonsCurrentlyInCreations:存了一个Xbeanname;存了一个Ybeanname;

    singletonFactories:存了一个key为Xbeanname,value为包含了Xbean的对象工厂;存了一个key为Ybeanname,value为包含了Ybean的对象工厂;

    到了Y的属性注入时,就会再进行X的实例化,不过不同的是:

     我们现在可以在这条代码中获取到Xbean,我们打开看下:

     可以看到,虽然singletonObjects里依旧没有Xbean,但是它会从earlySingletonObject集合里取,前面的过程并没有往里存数据,所以必然也取不到,接着会在singletonFactories取对应的X对象工厂,执行getObject(),执行的对应方法就是执行getEarlyBeanReference(beanName, mbd, bean)),返回一个exposeObject,这里的exposeObject其实就已经执行过各种自定义接口的了,可以算是完成了什么周期的bean对象了,同时把这个bean对象存在earlySingletonObjects,这样以后再取的话就不需要重新执行一遍程序了;

    到此,就已完成了spring里的循环依赖了啦,这里提到的几种集合其实就对应着大多数资料里提到的一二三级缓存:

    一级缓存:singletonObjects,我们常说的在‘容器’取到某个bean实例对象,其实就是从这里拿,但容器并不是指的就是这个单例池,它们之间的关系是容器>单例池;

    二级缓存:singletonFactories,这个是对象工厂的集合,之所以用工厂来包装原来的对象,为的就是可以通过这种反射的方式来使得对象变成bean对象,完成各种自定义,这也是能实现spring循环依赖最本质的东西;

    三级缓存:earlySingletonObjects:这个存的其实就只是已实例化完成的各种bean对象,这样每次取的时候就不需要再重新执行一遍程序了,提高效率

  • 相关阅读:
    linux学习之路(2)
    Cocos.js
    BOM常用对象
    display取值和应用
    DOM
    cursor属性
    visibilty属性
    打开新连接的方式
    JQuery
    js的创建对象
  • 原文地址:https://www.cnblogs.com/lzj-learn/p/14340860.html
Copyright © 2020-2023  润新知