什么是循环依赖

  1. @Service
  2. public class AService {
  3. @Autowired
  4. private BService bService;
  5. }
  6. @Service
  7. public class BService {
  8. @Autowired
  9. private AService aService;
  10. }

这其实就是 Spring 环境下典型的循环依赖场景,但并非所有循环依赖,Spring 都可以解决。针对构造器注入的循环依赖以及原型模式的 Bean 的循环依赖(因为每次都是创建一个新对象,无法利用缓存,容器虽然可以启动成功,但真正实例化 Bean 的时候还是会抛出异常),Spring 是无法解决的:

  1. @Service
  2. public class AService {
  3. public AService(BService b) {}
  4. }
  5. @Service
  6. public class BService {
  7. public BService(AService a) {}
  8. }

针对这种情况,Spring 在启动时会抛出 BeanCurrentlyInCreationException 异常。抛出异常的根本原因是 Spring 解决循环依赖依靠的是 Bean 的“中间态”这个概念,而这个中间态指的是已经实例化,但还没初始化的状态,而构造器正是用来完成实例化的,所以构造器的循环依赖无法解决。

针对构造器注入的循环依赖,可以改为属性或字段注入,或使用 @Lazy 延迟注入。比如如下代码:

  1. @Service
  2. public class AService {
  3. public AService(@Lazy BService service) {}
  4. }
  5. @Service
  6. public class BService {
  7. public BService(@Lazy AService service) {}
  8. }

其实,这种 @Lazy 方式注入的就不是实际的类型了,而是代理类,获取的时候会通过代理去拿值(实例化)。所以它可以解决循环依赖无法实例化的问题。
image.png

如何解决循环依赖

在实例化 Bean 的过程中,Spring 采用了三级缓存机制来解决 Spring Bean 的循环依赖问题:

  1. public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
  2. // 一级缓存,用于存放完全初始化好的bean,从该缓存中取出的bean可以直接使用
  3. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  4. // 二级缓存,提前曝光的bean,存放原始的bean对象(尚未填充属性),用于解决循环依赖
  5. private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
  6. // 三级缓存,缓存单例对象工厂,存放bean工厂对象,用于解决循环依赖
  7. private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  8. }

Bean 的实例化过程如下图所示:
image.png
到目前为止,发现使用二级缓存似乎就能解决循环依赖的问题。但 Spring 还提供了 AOP(面向切面编程)的功能,会动态增强对象,不管使用 JDK 的动态代理和 Cglib 动态代理,都会生成一个全新的对象。下图中我标出了 AOP 动态增强的位置。
image.png
此时就会出现一个问题,因为经过 AOP 以后,生成的是增强后的 bean 对象,也就是一个全新的对象,我们可以看到经过图中的流程后,单例池中会存在两个 bean:增强后的 a、b 对象,此时 a 对象中依赖的 b 为增强后的对象,而 b 对象依赖的 a 是为原始对象,未增强的。所以使用二级缓存解决不了循环依赖中发生过 aop 的这类引用问题。

为了解决二级缓存中 AOP 生成新对象的问题,Spring 中的解决方案是:提前 AOP,如果我们能够提前 AOP 就能解决上面的问题了,提前 AOP 指的就是,在加载 B 的流程中,如果发生了循环依赖,就是说 b 又依赖了 a,我们就要对 a 执行 aop,提前获取增强以后的 a 对象,这样 b 对象依赖的 a 对象就是增强以后的 a 了。三级缓存的 key 是 beanName,value 是一个 lambda 表达式,这个 lambda 表达式的作用就是进行提前 AOP。

下面是加入了三级缓存和 AOP 的流程图:
image.png
上面就是三级缓存的作用,其中有个三级缓存到二级缓存的升级过程,这个非常重要,主要是防止重复 aop。下面我们顺着源码分析:

1. doGetBean

  1. protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
  2. @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
  3. // 获取beanName
  4. final String beanName = transformedBeanName(name);
  5. Object bean;
  6. // 首先先尝试获取bean,如果加载过就不会在重复加载了
  7. Object sharedInstance = getSingleton(beanName);
  8. if (sharedInstance != null && args == null) {
  9. ...
  10. // 返回对应的实例,有时存在诸如FactoryBean的情况并不是直接返回实例本身而是返回指定方法返回的实例
  11. bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  12. } else {
  13. // 只有在单例情况下才会尝试解决循环依赖
  14. if (isPrototypeCurrentlyInCreation(beanName)) {
  15. throw new BeanCurrentlyInCreationException(beanName);
  16. }
  17. ......
  18. try {
  19. // 根据beanName获取eanDefinition对象
  20. final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
  21. ......
  22. // 单例模式创建bean
  23. if (mbd.isSingleton()) {
  24. sharedInstance = getSingleton(beanName, () -> {
  25. try {
  26. // 创建bean实例
  27. return createBean(beanName, mbd, args);
  28. } catch (BeansException ex) {
  29. destroySingleton(beanName);
  30. throw ex;
  31. }
  32. });
  33. bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
  34. }
  35. // 原型模式创建bean
  36. else if (mbd.isPrototype()) {
  37. Object prototypeInstance = null;
  38. try {
  39. beforePrototypeCreation(beanName);
  40. prototypeInstance = createBean(beanName, mbd, args);
  41. } finally {
  42. afterPrototypeCreation(beanName);
  43. }
  44. bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
  45. }
  46. ......
  47. }
  48. }
  49. return (T) bean;
  50. }

在具体实例化 Bean 的过程中,Spring 首先调用 getSingleton 方法尝试从单例缓存池中获取对应实例,因为在创建单例 Bean 的时候会存在依赖注入的情况,而在创建依赖时为了避免循环依赖,Spring 创建 Bean 的原则是不等 Bean 创建完成就会将创建 Bean 的 ObjectFactory 提早曝光,也就是将 ObjectFactory 加入到缓存中,一旦下个 Bean 创建时要依赖上个 Bean 则直接使用缓存的 ObjectFactory。具体代码如下:

2. getSingleton

  1. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  2. // 首先去一级缓存中获取,如果获取的到说明bean已经存在,直接返回
  3. Object singletonObject = this.singletonObjects.get(beanName);
  4. // 如果一级缓存中不存在,则去判断该bean是否在创建中,如果该bean正在创建中,说明此时发生了循环依赖
  5. if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  6. synchronized (this.singletonObjects) {
  7. // 如果发生循环依赖,首先去二级缓存中获取,如果获取到则返回,这个地方就是获取aop增强以后的bean,因为执行aop后会将其从三级缓存升级到二级缓存
  8. singletonObject = this.earlySingletonObjects.get(beanName);
  9. // 如果二级缓存中不存在,且允许提前访问三级引用
  10. if (singletonObject == null && allowEarlyReference) {
  11. ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
  12. if (singletonFactory != null) {
  13. // 如果三级缓存中的lambda表达式存在,执行aop,获取增强以后的对象,为了防止重复aop,将三级缓存删除,升级到二级缓存中
  14. singletonObject = singletonFactory.getObject();
  15. this.earlySingletonObjects.put(beanName, singletonObject);
  16. this.singletonFactories.remove(beanName);
  17. }
  18. }
  19. }
  20. }
  21. return singletonObject;
  22. }

先从一级缓存 singletonObjects 中去获取,如果获取到就直接返回。如果获取不到或者对象正在创建中,那就再从二级缓存 earlySingletonObjects 中获取,如果获取到就直接返回。如果还是获取不到,就从三级缓存 singletonFactories 中获取。如果获取到就从 singletonFactories 中移除,并且放进 earlySingletonObjects。其实也就是从三级缓存移动到了二级缓存中。

3. creatBean

如果从缓存中获取不到,则会执行单例 Bean 的实例化过程,核心逻辑在 creatBean 中。在 creatBean 方法中,真正创建 Bean 的逻辑在 AbstractAutowiredCapableBeanFactory 的 doCreateBean 方法:

  1. protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
  2. BeanWrapper instanceWrapper = null;
  3. if (mbd.isSingleton()) {
  4. instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
  5. }
  6. if (instanceWrapper == null) {
  7. // 根据指定bean使用对应的策略创建新的实例,如:工厂方法、构造函数自动注入、简单初始化
  8. instanceWrapper = createBeanInstance(beanName, mbd, args);
  9. }
  10. final Object bean = instanceWrapper.getWrappedInstance();
  11. Class<?> beanType = instanceWrapper.getWrappedClass();
  12. if (beanType != NullBean.class) {
  13. mbd.resolvedTargetType = beanType;
  14. }
  15. ......
  16. // 是否需要提早曝光:单例、允许循环依赖、当前bean正在创建中,则允许提前曝光
  17. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
  18. isSingletonCurrentlyInCreation(beanName));
  19. if (earlySingletonExposure) {
  20. // 为避免后期循环依赖,可以在bean初始化完成前将创建实例的 ObjectFactory 加入到三级缓存
  21. // getEarlyBeanReference中主要应用SmartInstantiationAwareBeanPostProcessor处理器,AOP的动态织入就是在这里完成的
  22. addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  23. }
  24. Object exposedObject = bean;
  25. try {
  26. // 对bean进行填充,将各个属性值注入,其中可能存在依赖于其他bean的属性,则会递归初始依赖bean,这是循环依赖发生的位置
  27. populateBean(beanName, mbd, instanceWrapper);
  28. // 执行bean的初始化方法以及后置处理器,此时如果有aop增强的逻辑,得到的exposedObject就已经是增强后的对象了
  29. exposedObject = initializeBean(beanName, exposedObject, mbd);
  30. }
  31. if (earlySingletonExposure) {
  32. // 这个我们可以看上面getSingleton方法,该方法的参数为false,就说明只允许去一级缓存和二级缓存获取,此时bean在创建中,一级缓存一定没有,就看二级缓存能不能获取到了
  33. Object earlySingletonReference = getSingleton(beanName, false);
  34. if (earlySingletonReference != null) {
  35. // 如果二级缓存获取到了,则说明提前执行了aop,因为执行了ObjectFactory才会从三级缓存移动到二级缓存
  36. if (exposedObject == bean) {
  37. // exposedObject就是要放入单例池中的对象,如果提前执行了aop,则将exposedObject对象替换为aop以后的对象
  38. exposedObject = earlySingletonReference;
  39. }
  40. ......
  41. }
  42. }
  43. return exposedObject;
  44. }

实际上,Spring 解决循环依赖的关键就在于 singletonFactories 这个三级缓存中。它里面缓存的是 ObjectFactory,它是解决问题的关键。

  1. // 它可以将创建对象的步骤封装到ObjectFactory中 交给自定义的Scope来选择是否需要创建对象来灵活的实现scope
  2. @FunctionalInterface
  3. public interface ObjectFactory<T> {
  4. T getObject() throws BeansException;
  5. }

循环依赖总结

1、getBean(A) 先去单例池获取,单例池不存在,二级缓存获取,二级缓存不存在且允许提前访问,三级缓存中取,此时返回为空,开始加载 A

2、singletonsCurrentlyInCreation(A) 将 A 放入正在创建的 Map 中

3、new A(); 实例化 A

4、提前暴露 A,将 A 放入三级缓存,addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

5、设置属性 populateBean(beanName, mbd, instanceWrapper);

6、发现 A 依赖 B,需要先创建 B

7、getBean(B)

8、先去单例池获取 B,单例池不存在,二级缓存获取,二级缓存不存在且允许提前访问,三级缓存中取,此时返回为空,开始加载 B

9、将 B 放入 singletonsCurrentlyInCreation() 的 Map 中

10、new B() 实例化 B

11、将 B 放入三级缓存 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

12、设置属性 populateBean(beanName, mbd, instanceWrapper);

13、发现 B 依赖 A

14、getBean(A)

15、发现三级缓存中存在 A,getEarlyBeanReference(A, mbd, bean) 获取 A,同时把 A 放入二级缓存,删除三级缓存

16、执行 B 的 initializeBean 方法,执行 aop,获取增强以后的引用

17、B 创建完了,将 B 放入单例池冲

18、继续执行第 7 步,返回的 getBean(B)就是创建好的 B

19、接下来 A 初始化

20、因为 A 的三级缓存中的 getEarlyBeanReference(beanName, mbd, bean) 被 B 已经执行过了

21、A 就能从二级缓存中获取自己的引用

22、如果发现引用变了,此时 A 就指向二级缓存中的引用

23、将 A 放出单例池中

24、删除二级缓存和三级缓存
image.png