Spring IOC循环依赖问题

发布于:2022-11-28 ⋅ 阅读:(363) ⋅ 点赞:(0)

Spring IOC循环依赖问题

什么是循环依赖

循环依赖其实就是循环引用, 也就是两个或者两个以上的Bean互相持有对方,形成闭环,例如:A依赖B,B依赖C,C又依赖于A.

Spring中循环依赖的场景有:

  • 构造器的循环依赖(构造器注入)
  • Field属性的循环依赖(Set注入)

其中,构造器的循环依赖问题是无法解决的,只能抛出异常BeanCurrentlyInCreationException异常,在解决属性循环依赖时.Spring采用的是提前暴露对象的方法. 这也就解决了其中一个依赖的问题,继而解决了循环依赖的问题.

Spring IOC处理循环依赖的方式

在Spring IOC中主要是通过三级缓存机制来处理循环依赖问题.

  • 第一季缓存singletonObjects:单例池(key->beanName, value->Bean)
    • 这个缓存中都是一些已经实例化好的对象,不存在依赖问题.
  • 第二季缓存:earlySingletonObjects(key->beanName, value->Bean)
    • 已经实例化但还没有进行属性注入的Bean,由三级缓存放入.
  • 第三季缓存:singletonFactories(key->beanName, value->ObjectFacotry)
    • singletonFactory.getObject()出来的对象.该对象还未开始依赖或者暂未依赖完全.

示例:ClassA依赖ClassB,ClassB依赖ClassA,这样就构成了循环依赖,Spring IOC的解决方案如下:

Spring IOC解决循环依赖的理论依据是基于Java的引用传递,当获得对象的引用时,对象的属性可以延后设置.

但是,构造器必须时在获取引用之前,Spring通过Set或者@Autowired方法解决循环依赖其实是通过提前暴露一个ObjectFactory对象来完成的,简单来说ClassA在调用构造器完成初始化之后,在调用setClassB方法之前就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring容器中.

  • Spring容器初始化ClassA通过构造器初始化对象后提前暴露到Spring容器中

  • ClassA调用setClassB方法,Spring首先尝试从容器中获取ClassB, 此时ClassB不在Spring容器中
  • Spring容器开始初始化ClassB,同时也会将ClassB提前暴露在Spring容器中
  • ClassB调用setClassA方法,Spring从容器中获取ClassA,因为ClassA已经暴露,此时可以从第三级缓存中获取到ClassA,这样就完成了ClassB的实例化.
  • ClassA这个时候通过Spring容器获取ClassB实例,完成ClassA的实例化.

这个时候,ClassA和ClassB都完成对象的初始化,解决了循环依赖的问题.

什么情况下无法解决循环依赖

  • 单例Bean
    • 构造器注入形成的循环依赖无法解决
    • Set注入形成的循环依赖可以解决
    • @Autowired注解方式注入形成的循环依赖可以解决
  • prototype原型的Bean(多例)
    • 对于原型bean的初始化过程中不论是那种情况产生的循环依赖,Spring都会直接报错处理.

参考: Spring IOC循环依赖问题_蹦跶的蜗牛的博客-CSDN博客_springioc循环依赖Spring IOC循环依赖问题_蹦跶的蜗牛的博客-CSDN博客_springioc循环依赖Spring IOC循环依赖问题_蹦跶的蜗牛的博客-CSDN博客_springioc循环依赖

本文含有隐藏内容,请 开通VIP 后查看