zoukankan      html  css  js  c++  java
  • Spring循环依赖解决方式源码解析

    1. 什么是循环依赖?

    循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于A我们直接上代码

    先创建一个类ServiceA依赖于ServiceB,然后ServiceB又依赖于ServiceA

    @Component
    public class ServiceA {
    
        @Autowired
        private  ServiceB serviceb;
    
    @Component
    public class ServiceB {
    
        @Autowired
        private  ServiceA servicea;

    2.Spring的单例对象的初始化主要分为三步:

    1. createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象

    2. 填充属性,这一步主要是多bean的依赖属性进行填充

    3. 调用实现了InitializingBean的接口,实现afterPropertiesSet方法,或者调用在配置文件中同过init-method的方法。

    4.  从上面单例bean的初始化可以知道:循环依赖主要发生在第一、二步,也就是构造器循环依赖和field循环依赖。那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存。

          这里会涉及到在spring内部所使用的两个内部属性,singletonFactories和earlySingletonObjects,这两个属性在类DefaultSingletonBeanRegistry中被定义,定义如下:

    下面我们就看下spring创建单例bean的步骤吧

    第一次创建ServiceA的步骤如下

    org.springframework.beans.factory.support.AbstractBeanFactory#getBean方法在进入

    由于是首次创建ServiceA在,singletonFactories和earlySingletonObjects和singletonFactories都不存在。所以返回了null。

    然后通过反设创建了一个serviceA的BeanWrapper,然后在调用org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory这时候我们singletonFactories就存在serviceA了

    紧接着就是调用populateBean填充ServiceA的属性了,由于ServiceA有属性ServiceB,此时我们的ServiceB还没有被加载,然后ServiceA有需要ServiceB于是spring又去调用了创建ServiceB的方法

    于是我们接着继续填充ServiceB的属性,由于ServiceB又依赖ServiceA,于是再去创建ServiceA由于此时ServiceA已经在singletonFactories里面了,然后我们就返回了一个是ServiceA(此时的属性serviceB是为null的),并且ServiceA从singletonFactories里面移除掉加入到earlySingletonObjects。

    这样做有什么好处呢?让我们来分析一下“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况。A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化。

    知道了这个原理时候,肯定就知道为啥Spring不能解决“A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象”这类问题了!因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。

    参考:https://blog.csdn.net/u010853261/article/details/77940767

  • 相关阅读:
    JavaScript模态对话框类
    事件模块的演变(1)
    html5中可通过document.head获取head元素
    How to search for just a specific file type in Visual Studio code?
    What do 'lazy' and 'greedy' mean in the context of regular expressions?
    正则非获取匹配 Lookahead and Lookbehind ZeroLength Assertions
    regex length 正则长度问题
    Inversion of Control vs Dependency Injection
    How to return View with QueryString in ASP.NET MVC 2?
    今天才发现Google Reader
  • 原文地址:https://www.cnblogs.com/root429/p/12799238.html
Copyright © 2011-2022 走看看