Spring的三级循环及循环依赖

zjun Lv4

循环依赖

循环依赖是指两个对象相互依赖,形成了一个环形的调用链路。比如下面的例子:

1
2
3
4
5
6
@Component public class A { 
@Autowired B b;
}
@Component public class B {
@Autowired A a;
}

上面的例子中,当创建A对象时,发现依赖B,转而去创建B对象,但在创建B对象时又发现依赖A,从而周而复始,形成了一个循环依赖。
像这样的例子在实际的开发过程中是很难避免甚至是常见的,但我们在使用Spring的过程中却并不会遇到循环依赖的问题,这是为什么呢?答案就是Spring中通过使用三级缓存来避免了出现循环依赖的问题。

Spring中Bean的生命周期

在开始讲解Spring如何通过三级缓存来避免循环依赖之前,我们先来了解一下Spring中的Bean的生命周期。
Spring在初始化Bean时,BeanFactory会根据BeanDefinition创建Bean对象,创建的过程如下:

  • 实例化:实例化该Bean对象
  • 填充属性:给该Bean赋值
  • 初始化
    • 如果实现了Aware接口,会通过其接口获取容器资源
    • 如果实现了BeanPostProcessor接口,则会回调该接口的前置和后置处理增强
    • 如果配置了 init-method 方法,会执行该方法
  • 销毁
    • 如果实现了 DisposableBean 接口,则会回调该接口的 destroy 方法
    • 如果配置了 destroy-method 方法,则会执行 destroy-method 配置的方法

Spring使用三级缓存解决循环依赖

Spring的三级缓存分别是:

  • 第一级缓存:用来保存实例化,初始化都完成的对象
  • 第二级缓存:用来保存实例化完成,但是未初始化完成的对象
  • 第三级缓存:用来保存一个对象工厂,提供一个匿名内部类,用于创建二级缓存中的对象
    三级缓存的核心逻辑就是:把实例化和初始化的步骤分开,然后放入缓存中,供另一个对象调用。
    Spring解决循环依赖有两个前提条件:
  • 不全是构造器方式的循环依赖(否则无法分离初始化和实例化的操作)
  • 必需是单例(否则无法保证是同一对象)

一个例子

我们以上面提到的例子来看一下三级缓存是怎么解决循环依赖的:但A,B两个类发生循环引用时。

  • A完成实例化后,去创建一个工厂对象,并放入三级缓存当中
    • 如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象
    • 如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象
  • A进行属性注入时,去创建B
  • B进行属性注入时,需要A,则从三级缓存中去取A工厂代理获取A的早期对象,并将这个早期对象注入二级缓存,然后删除三级缓存中的A工厂,将尚未创建完毕的A的引用赋值给B
  • B完成后续属性注入,直到初始化结束,将B放入一级缓存
  • A从一级缓存中取到B并且注入B,直到完成后续操作,将A从二级缓存删除,并放入一级缓存,循环依赖结束

为什么要使用三级缓存

从解决循环依赖的核心逻辑:把实例化和初始化的步骤分开,然后放入缓存中来看,其实只使用二级缓存就可以解决了(一级缓存存放实例化和初始化完成的实例,二级缓存存放实例化完成但尚未初始化完成的对象),那么为什么还需要引入一个三级缓存呢?
这是因为Spring中还有另外一个问题需要解决,就是初始化过程中的AOP实现,Spring中的AOP有两种实现方式:一种是JDK自带基于接口的动态Proxy技术,另一种是CGlib基于字节码动态生成的Proxy技术。Spring中AOP的实现是通过后置处理器 BeanPostProcessor 来实现的,而后置处理器是在填充属性结束后才执行的。

  • 实例化对象
  • 对象填充属性
  • BeanPostProcessor doBefore
  • init-method
  • BeanPostProcessor doAfter – AOP在这个阶段实现

所以如果循环依赖的对象中有AOP代理的对象的话,通过二级缓存就不容易解决循环依赖的问题。如果只使用二级缓存解决循环依赖,那么意味着所有的 Bean 在实例化后就要完成AOP代理。

三级缓存只会创建一个工厂对象并将其放入三级缓存中,也就是说只有在真正方式循环依赖时才会去生成代理对象。

  • 标题: Spring的三级循环及循环依赖
  • 作者: zjun
  • 创建于 : 2019-08-02 22:31:22
  • 更新于 : 2023-12-06 11:48:55
  • 链接: https://zjun.site/2019/08/4c2d0cec00e5.html
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论