分享

UC头条:spring

 灬渔游水清清灬 2020-02-18

首先看下spring创建一个bean的简单流程,假如beanA引用beanB,beanB引用beanA,spring在初始化beanA的时候会造成循环依赖(这里讲的是单例,spring底层只对单例循环依赖进行解决)。

在记录之前我写了两个测试类进还原循环依赖,一个是ClassA,里面的引用了ClassB,同时ClassB也引用了ClassA。这样子ClassA和ClassB形成了循环依赖。


spring容器底层在创建ClassB Bean会调用getSingleton先去从一级缓存singletonObjects中拿,如果一级缓存没有,则去二级缓存earlySingletonObjects中拿,二级缓存中没有,则去三级缓存singletonFactories中拿,如果都没有,则调用createBean方法开始创建这个ClassB这个Bean。

在调用 createbean之前spring会调用isPrototypeCurrentlyInCreation方法来判断当前这个bean存在正在创建中的缓存中(prototypesCurrentlyInCreation),如果不是则放入该缓存中,用于后面循环依赖的解决。因为ClassB是第一次被spring容器加载,所以肯定是空,这时候被放入正在创建中的缓存中。

接着createbean会调用doCreateBean方法,这个真正执行创建bean的方法。该方法调用createBeanInstance(beanName, mbd, args)通过后置处理器判断调用ClassB的构造方法并创建返回ClassB的实例对象,此时对象的ClassA的引用肯定是空,因为ClassB的默认构造方法,并没有对ClassA赋值。接着spring会将这个早期对象放入三级缓存singletonFactories中。

放入三级缓存singletonFactories结束后,调用populateBean方法进行属性赋值。populateBean中会去判断ClassB这个Bean有哪些属性以及属性的类型并选择调用哪个方法来进行赋值,如果是引用类型则调用resolveReference方法进行属性赋值。

进入resolveReference方法,spring会先去判断当前容器是否有父容器,如果有则从父容器中获取引用对象ClassA,如果没有则从当前容器中获取引用对象ClassA。(spring允许子容器使用父容器的bean,就是在这里体现出来,比如springmvc)这时候程序会调用this.beanFactory.getBean(resolvedName)。从容器中再次获取ClassA这个引用对象。此时getBean会调用doGetBean从新走刚才创建ClassB对象流程。


  

同样spring在创建ClassB的引用对象ClassA时,也会去解析ClassA的引用对象。此时ClassA的引用对象是ClassB(此时ClassB对象是暴露在三级缓存中的),这是时候通用调用当前容器的getBean--->doGetBean,在doGetBean中调用getSingleton(beanName)方法。在这里我们会看到spring在从二级或三级缓存中获取对象是有条件的,条件即使这个对象正在创建中。通过isSingletonCurrentlyInCreation(beanName)这个方法去判断的。

  

确实刚创建ClassB的时候记录了ClassB正在创建中,这时候从一级缓存中获取(肯定没有因为ClassB还没完全实例化完),接着继续从二级缓存中获取也没有(因从刚才分析下来,ClassB只单单暴露在三级缓存中),这时候调用singletonObject = singletonFactory.getObject这个方法,从三级缓存中拿到了早期对象。singletonFactory.getObject会进一步调用getObject方法中的getEarlyBeanReference,这里面调用了spring的后置处理器,给开发者对早期对象能够进行提前的修改。比如ClassB 对象里有String str="";可以提前对str进行操作。所以为什么spring有二级缓存就可以解决循环依赖。还要用到三级缓存。spring在三级缓存给开发提供了给早期对象扩展的功能。此时将扩展完的早期对象ClassB放入二级缓存,移除三级缓存,并将这个对象返回出去。

 

此时ClassA对象ClassB的引用已经拿到值了,所以创建一直往下走,走到addSingleton(beanName, singletonObject)这个方法,将ClassA放入一级缓存singletonObjects,从二级缓存和三级缓存中移除。因为ClassA的bean已经创建完成了,二级缓存和三级缓存已经没用了。此时完整的ClassA的bean返回出去,ClassB的属性ClassA也得到了赋值,ClassB的bean可以继续创建了。

 

注意:spring在通过构造器给属性赋值是无法解决循环依赖的,从上面分析来看spring执行createBeanInstance方法去判断调用当前bean的构造方法,此时还未放入三级缓存中。如果是bean作用域是原型也是无法解决循环依赖,因为原型对象并没有放在缓存中。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多