Spring循环依赖

、前言

Bean的初始化过程

Spring循环依赖_第1张图片

Spring循环依赖_第2张图片

Spring循环依赖是面试考点之一。面试官可以深挖面试者对Spring关于Bean的生命周期掌握情况。Spring循环依赖也是Spring难点之一,逻辑比较绕,需要对Spring Bean的生命周期了如指掌。
二、什么是循环依赖?

在这里插入图片描述
简单,就是A对象依赖了B对象,B对象依赖了A对象。

@Component
public class A {
    @Autowired
    B b;
}


@Component
public class B {
    @Autowired
    A a;
}

 

三、Spring是如何处理循环依赖?
3.1 使用文字描述具体循环依赖的两个Bean的生命周期流程

A 生命周期
1. class文件
2. 通过beanName去缓存中获取对象。一开始,单例池没有,且a也没有处于正在创建集合中,所以不会去二级缓存、三级缓存拿对象,相关代码[标记①]
3. 把对象放到正在创建集合中(beforeSingletonCreation(beanName);) 相关代码[标记②]
4. 通过反射实例化对象(createBeanInstance();) 相关代码[标记③]
5. 添加对象工厂到二级缓存中 相关代码[标记④]
6. 属性填充。在这里发现需要B对象,从单例池获取B->null->且b也没有处于正在创建集合中->实例化B且完成B的生命周期 相关代码[标记⑤]
    wait for b init...
7. 初始化   相关代码[标记⑥]
8. 从二级缓存中获取Bean  相关代码[标记⑦]
9. 添加到单例池 相关代码[标记⑥]

B 生命周期
1. class文件
2. 通过beanName去缓存中获取对象。一开始,单例池没有,且a也没有处于正在创建集合中,所以不会去二级缓存、三级缓存拿
3. 把对象放到正在创建集合中(beforeSingletonCreation(beanName);)
    (singletonObject = singletonFactory.getObject();)
4. 通过反射实例化对象(createBeanInstance();)
5. 添加对象工厂到二级缓存中
6. 属性填充。在这里发现需要A对象,从单例池获取A->null->此时a在正在创建集合中->从三级缓存中获取对象(如果a存在切面,则返回代理对象),将对象放至二级缓存,删除三级缓存
7. 初始化
8. 从二级缓存中获取Bean
9. 添加到单例池

  


3.2 为什么需要三级缓存来解决循环依赖呢?
三级缓存定义及作用

    一级缓存: singletonObjects。存储已走完Spring生命周期的Bean。注意,对象和Bean在Spring的世界是完全两个概念。通过反射实例化的叫对象,此时这个对象并未完成Spring的生命周期流程(如后置处理器等),Bean则表示已经完成Spring整个生命周期流程。
    二级缓存: earlySingletonObjects。存储已实例化(通过反射创建的对象)但是未走完Spring生命周期流程的对象。
    三级缓存: singletonFactories。存储对象工厂。目的是在适当的时候通过对象工厂生成AOP代理对象。

如何解决

    通过提前暴露自己的方式(将自己加入三级缓存)让依赖者注入,从而不会导致死循环,可以走完剩下流程。
    暴露对象工厂目的是为了完成AOP代理。对象工厂清楚如何创建对象的AOP代理,但是不会立马创建,而是到合适的时机进行AOP代理对象的创建。
    二级缓存存在的目的之一是保证对象只有一次AOP代理。当调用三级缓存的getObject()方法返回的对象会存入二级缓存,这样,当接下来的依赖者调用的时候, 会先判断二级缓存是否有目标对象,如果存在直接返回。

 

四、不同注入方式与循环依赖关系
Spring循环依赖_第3张图片

  

注: A先创建。

小结:

    主对象(具备循环依赖且最先加载的类,简称A)不能通过构造函数的方法注入所依赖的Bean对象(简称B),而B不受限制,可以使用任意方式注入(setter、@Autowire、构造函数)。
    循环依赖中A对象不能使用构造函数注入的原因是在AbstractAutowireCapableBeanFactory#createBeanInstance方法中做类型推断时,如果推断是以构造方法实例B,则会直接调用``AbstractAutowireCapableBeanFactory#autowireConstructor,该方法会通过调用AbstractAutowireCapableBeanFactory#doCreateBean创建对象,但是由于A对象没有提前暴露工厂(不给机会呀),所以只能一步步创建,但是走到代码DefaultSingletonBeanRegistry#getSingleton的beforeSingletonCreation方法时,由于此时A正在创建集合,强行添加则会返回false`,则抛出异常。相关代码③
 

    小结:

        主对象(具备循环依赖且最先加载的类,简称A)不能通过构造函数的方法注入所依赖的Bean对象(简称B),而B不受限制,可以使用任意方式注入(setter、@Autowire、构造函数)。
        循环依赖中A对象不能使用构造函数注入的原因是在AbstractAutowireCapableBeanFactory#createBeanInstance方法中做类型推断时,如果推断是以构造方法实例B,则会直接调用``AbstractAutowireCapableBeanFactory#autowireConstructor,该方法会通过调用AbstractAutowireCapableBeanFactory#doCreateBean创建对象,但是由于A对象没有提前暴露工厂(不给机会呀),所以只能一步步创建,但是走到代码DefaultSingletonBeanRegistry#getSingleton的beforeSingletonCreation方法时,由于此时A正在创建集合,强行添加则会返回false`,则抛出异常。相关代码③

如果解决循环依赖呢?

    延迟构造
    使用setter注入

你可能感兴趣的:(面试题)