0%

SpringBean循环依赖及解决

文章字数:770,阅读全文大约需要3分钟

一、什么是循环依赖

循环依赖就是两个或者以上的bean相互持有对方,形成了一个依赖闭环。

循环依赖的场景有

  1. 构造器的循环依赖
  2. field属性的循环依赖

二、检测循环依赖

  1. Spring创建Bean时是递归创建的,即发现属性有依赖的Bean就回去创建属性对象的Bean
  2. 检测循环依赖就是在创建Bean的时候做个标记,当再次递归调用创建的时候,发现有创建的标记就知道了

三、解决循环依赖

  1. 三级缓存

    1
    2
    3
    4
    5
    6
    7
    8
    /** 存放单例bean 1级*/
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

    /** 存放初始化,但还没赋值的bean的工厂(存放函数接口、钩子函数等) 3级*/
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

    /** 存放提前曝光的bean 2级*/
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
  2. bean实例化过程

  • createBeanInstance实例化bean
  • populateBean填充属性
  • initializeBean初始化bean

依赖循环发送在实例化bean(调用构造方法)和填充属性(递归创建bean)的时候

  1. 创建流程
  • 执行构造函数,创建一个未初始化的类
  • 将初始化的类暴露到singletonFactories中,并标记已创建(加入registeredSingletons)
  • 填充属性,属性中包含其他bean则递归进入创建流程
  • 递归创建的属性假如也依赖最早的类,则会先尝试获取它
  • 尝试在一级缓存(singletonObjects)获取
  • 当获取不到,且改bean被标记过正在创建尝试在二级缓存(earlySingletonObjects)中获取提前暴露的类
  • 找不到则从三级缓存(singletonFactories)获取。
  • 此时就找到了创建但未初始化的类
  • 找到后将三级缓存中的类工厂移除,将类添加到提前曝光的二级缓存中earlySingletonObjects
  • 然后返回本身,此时最初的类获取到属性的bean后也能创建成功了。
  • 四、三级缓存的意义

  1. 二级缓存也可以解决循环依赖,一个存正常的单例,一个存提前曝光的类
  2. 三级缓存主要是在曝光且提前引用时能够做一下拓展操作,二级缓存只能知道提前曝光,三级缓存转移到二级缓存才是曝光并提前引用
  • 每个对象创建之后会放在三级缓存中,暴露自己的构造工厂
  • 然后执行初始化方法,填充需要的依赖。
  • 递归创建依赖对象时,如果发现需要注入的对象是之前递归过程中创建并初始化的(二级缓存中的),或者是创建未初始化的(三级缓存中),就从缓存中拿出来
  • 如果是从三级缓存拿的,从三级缓存中移除,放到二级缓存中

关键词: java, spring