• 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

  • 相关阅读:
    Assembly Manifest 通俗简易手册
    CruiseControl服务器安装配置
    关于URL编码
    从A到Z来说说Web开发
    通过注册表查看 .NET Framework的版本信息
    云数据存在哪里?
    C#中你可能不知道的8件事(zz)
    用PBKDF2 或BCrypt 来存储密码
    C++编译器什么时候为我们自动生成拷贝构造函数?
    C#中你可能不知道的8件事(zz)
  • 原文地址:https://www.cnblogs.com/root429/p/12799238.html
Copyright © 2020-2023  润新知