Loading...
墨滴

Roy4259

2021/12/29  阅读:40  主题:嫩青

Spring如何解决循环依赖

Spring如何解决循环依赖

  1. 循环依赖是什么
  2. Spring Bean的生命周期
  3. 为什么会有循环依赖
  4. 怎么解决循环依赖
  5. 有什么拓展点
  6. 常见问题

1. 循环依赖是什么

简单的,Bean A 依赖 Bean B ,同时Bean B 也依赖Bean A 就属于循环依赖,成环了。

2. Spring Bean的生命周期

1' 创建 bean 的实例化,使用合适的实例化策略来创建 bean 的实例(工厂方法、构造函数)

2' 对 @Autowired@Value 注解预解析

3' 把这个时候的 bean (称为早期对象),放入到三级缓存中

4' 对属性进行赋值

5' 对象初始化

    1. 回调调用 aware 接口方法(BeanNameAwareBeanClassLoaderAwareBeanFactoryAware)
    1. 调用 bean 初始化前的后置处理器方法(被 @PostConstruct 注解的方法)
    1. 调用初始化方法
      1. 调用 InitializingBeanafterPropertiesSet 方法
      1. 调用自定义的 initMethod 方法
    1. 调用 bean 初始化后的后置处理器方法
    1. 应用上下文销毁, Bean 实现了 DisposableBean 接口,就会调用 destory 方法(@destory-method 也可)

3. 为什么会有循环依赖

使用例子分析:A->B,B->A

  1. 创建A:创建实例化出一个早期对象...一直到属性赋值的时候发现了依赖Bean B;

  2. 这个时候要创建 B ,因为 B 还不存在;

  3. 创建 B ,创建实例化出一个早期对象...一直到属性赋值的时候发现了依赖Bean A,

  4. 这个时候 A 也并不在容器里边,也没有,就又去创建 A ,如此就造成了循环依赖

4. 怎么解决循环依赖

需要为上面的环设计一个出口:一个状态标识这个 Bean 正在创建,如果这个 Bean 正在创建就表示有循环依赖,需要跳出循环,同时需要一个容器存储早期的 Bean ,将早期的 Bean 返回回去属性注入,这样就能解决了.

但是,如果发现循环依赖的 Bean 使用了 AOP 呢,需要在属性注入的时候就把代理类注入进去,一般生成动态代理是在 Bean 初始化之后,但是这种情况需要提前为 Bean 创建代理,然后属性赋值进入被依赖的 Bean 中,上面显然没办法做到,需要再加上一个容器,在里面生成早期的 Bean 或者动态代理,JDK 8的函数式接口正好可以满足这个(通过bean后置处理器的 getEarlyBeanReference 方法,在里边判断并且生成).

5. 有什么拓展点

可以使用 SmartInstantiationAwareBeanPostProcessor 接口的 getEarlyBeanReference 方法进行拓展

6. 常见问题

1' Bean 创建的时候,多线程怎么保证获取到完整成熟的 Bean 使用二级缓存+双重检查锁来保证 第一次获取 Bean 没获取到会加锁,不让其他线程进入到下面从二三级缓存中获取 第一次没有获取到, 会走入创建 Bean 的流程, 但是在走创建流程前,会再次从以及缓存中获取,获取不到才会走创建流程,这个创建过程也是被锁锁起来的,意味着在我没有创建完之前,别的线程都只能阻塞等待,直到创建结束

2' 为什么使用三级缓存 解决AOP的循环依赖,用Bean的后置处理器解耦,直接在方法里判断生成

3' 能不能解决给构造器的循环依赖问题 不能, 构造器生成(也就是我们生成早期Bean的操作)的时候,还没有把早期的Bean放入到三级缓存中,没有办法通过三级缓存获取到早期Bean

4' 能不能解决多例 Bean 的循环依赖问题 不能,单例Bean的循环依赖解决实际上是依赖了缓存,且只能使用字段注入的方式 不能使用构造器的方式; 每次都需要创建新对象/构造方法注入是没有办法利用缓存的,也就是无法解决

5' 循环依赖可以关闭吗 可以,applicationContext.setAllowCircularReferences(false);

Roy4259

2021/12/29  阅读:40  主题:嫩青

作者介绍

Roy4259