这里写目录标题

  • 一.Bean的生命周期的过程
  • 二.Bean的循环依赖
  • 2.1 什么是循环依赖
  • 2.1 spring,什么场景下有循环依赖
  • 2.1.1 构造器注入循环依赖
  • 2.1.2 setter 属性注入循环依赖
  • 2.1.3 spring能解决循环依赖的情况
  • 2.2 spring解决单例的循环依赖---三级缓存
  • 2.2.1 "A-B,B-A"循环依赖分析
  • 2.2.2 为什么Spring不能解决“A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象”?
  • 2.2.3 为什么Spring不使用二级缓存?


一.Bean的生命周期的过程

单例管理的对象,支持延迟初始化、启动后立刻初始化,但是都是由spring去管理Bean的整个生命周期。

非单例管理的对象,是延迟初始化,由spring去管理Bean的创建和初始化,实例的使用和销毁由客户端控制。

java中怎么检测Bean循环依赖 bean的循环依赖_二级缓存


从上面可以看出,本质就是创建一个对象,那么创建对象具体包含:当前对象本身实例化【反射机制实现实例化】、对象属性的实例化【借用DI依赖注入,装配Bean属性及依赖关系】。

代理对象:A类—>生成一个普通对象–>属性注入–>基于切面生成一个代理对象–>把代理对象放入singletonObjects单例池中。【—后面研究代理对象】

二.Bean的循环依赖

2.1 什么是循环依赖

循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。
一般性的循环依赖,程序会一直执行下去最终导致内存溢出。

2.1 spring,什么场景下有循环依赖

对于单例对象初始化,核心的是三个方法:

java中怎么检测Bean循环依赖 bean的循环依赖_二级缓存_02


按照上面的三步骤,会出现循环依赖的主要在第一步【构造器注入】和第二步【field属性注入(包括了prototype field和setter方法)】。

2.1.1 构造器注入循环依赖

通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException。
此场景循环依赖抛出异常分析:Spring容器将每一个正在创建的bean标识符放在一个“当前创建bean池”中,bean标识符创建过程中将一直保持在这个池中,因为如果在创建bean过程中发现自己已经在“当前创建bean池”中时,将会抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的bean将从“当前创建bean池”中清除掉。

-----可通过@Lazy 注解来解决循环依赖。

2.1.2 setter 属性注入循环依赖

会将循环依赖隐藏。

2.1.3 spring能解决循环依赖的情况

但是,对于出现的循环依赖,spring能解决特定某些场景的循环依赖。
Spring能解决的循环依赖:单例对象的setter方法注入循环依赖
Spring不能解决的循环依赖:构造器注入循环依赖和prototype field属性注入循环依赖。

2.2 spring解决单例的循环依赖—三级缓存

Spring是不能解决构造器注入的循环依赖,会抛出异常。因为,进入三级缓存singletonFactories的前提是执行构造器。

singletonObjects一级缓存:单例对象的cache 【用于存放完全初始化好的 bean,从该缓存中取出的 bean可以直接使用】<beanName -> bean实例>
earlySingletonObjects二级缓存:提前曝光的单例对象的Cache 【存放原始的 bean 对象(尚未填充属性),用于解决循环依赖】
singletonFactories三级缓存: 单例对象工厂的cache 【存放 bean 工厂对象,用于解决循环依赖】<beanName -> beanFactory>

—细节说明:
一个对象只要实例化执行构造器成功后,会先进入singletonFactories三级缓存。
一个对象只要初始化完成后,会进入singletonObjects一级缓存。
earlySingletonObjects二级缓存的添加在getSingleton()方法:如果遇到“A-B,B-A”的循环依赖场景,先是从一级缓存查找,查不到再二级缓存、再三级缓存的过程,如果是从singletonFactories三级缓存找到,那么将这个未完成的对象Bean放入earlySingletonObjects二级缓存、并且删除singletonFactories三级缓存的记录。

2.2.1 "A-B,B-A"循环依赖分析

java中怎么检测Bean循环依赖 bean的循环依赖_初始化_03


对象A首先进行初始化第一步【实例化】,只要成功执行完构造器,就将自己提前曝光到singletonFactories三级缓存;

对象A进行初始化第二步【属性注入】,此时依赖对象B,但发现对象B还没有create,那么开始初始化B对象;

B对象初始化第一步时也将自己提前曝光到singletonFactories三级缓存;

接着B属性注入,依赖对象A,尝试从singletonObjects一级缓存获取,但肯定是没有的【A对象还未初始化完成】、再接着从earlySingletonObjects二级缓存中获取,也是没有、再继续从singletonFactories三级缓存中获取【对象A先是在singletonFactories三级缓存,然后进入二级缓存,并删除三级缓存中的记录】;

B拿到了A对象,顺利完成属性注入和初始化第三步。全部完成后,将对象B放入singletonObjects一级缓存【对象B在二级缓存、三级缓存就删除】。

返回A中,A对象拿到B对象引用,顺利完成后续初始化。

2.2.2 为什么Spring不能解决“A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象”?

因为,一个对象在加入singletonFactories三级缓存,首先需要完成实例化即执行构造函数方法,所以构造器循环依赖是无法解决。

2.2.3 为什么Spring不使用二级缓存?

如果仅仅是解决循环依赖问题,二级缓存也可以,但是如果注入的对象实现了AOP,那么注入到其他bean的时候,不是最终的代理对象,而是原始的。通过三级缓存的ObjectFactory才能实现类最终的代理对象。