(Spring)一文看懂循环依赖 --- 附相关源码

147 阅读4分钟

什么是循环依赖

先看一段代码

@Component
public class AService {
   
   @Autowired
   private BService bService;
}
@Component
public class BService {

   @Autowired
   private AService aService;
}

A中注入了B,B又注入了A,这就构成了循环依赖的条件。

Bean的生命周期与循环依赖

在Bean的生命周期:

  1. 扫包得到BeanDefinition
  2. 合并BeanDefinition
  3. 加载类进行实例化前操作
  4. 推断构造方法
  5. 实例化
  6. BeanDefinition后置处理器操作
  7. 实例化后
  8. 属性填充
  9. 属性填充后
  10. Aware回调
  11. 初始化前
  12. 初始化中
  13. 初始化后 我们在结合上述的例子,AService注入BService的这个过程是发生在8.属性填充这一步,此时就会根据BService属性的类型和名字去BeanFactory中获取B类所对应的单例bean,如果此时存在则直接拿来赋值给BService属性,不存在则去生成一个BService。

在这里spring加载是一个串行化的过程,BService是肯定没有被创建的于是就去创建BService又会走到8.属性填充这一步那么BService中注入了AService,但是AService他还在创建中并没有生成单例bean于是就形成了一个死循环。

具体过程:
创建AService-->依赖BService-->创建BService-->依赖AService-->AService还在创建过程中......

三级缓存

在Spring中,通过三级缓存来解决了部门循环依赖的问题。
一级缓存:singletonObjects(缓存的是经历了完整生命周期的bean对象也就是常说的单例池)
二级缓存:earlySingletonObjects(缓存的是生命周期还未完成的Bean对象)
三级缓存:singletonFactories(缓存的是ObjectFactory,表示对象工厂用来创建早期bean对象的)

解决循环依赖思路

上述:
创建AService-->依赖BService-->创建BService-->依赖AService-->AService还在创建过程中......
改造:
创建AService(同时将Aservice创建一个未完成生命周期的bean放入缓存)--> 依赖BService -->
创建BService-->依赖AService(从缓存中获取)-->BService创建好 -->赋值到ASerivce --> AService创建好 附图:

解决循环依赖.png 根据刚刚分析发现只需要二级缓存就可以解决循环依赖那么为什么还要引入三级缓存?
这里需要在考虑一种场景,假如引入了AOP,在13.初始化后这一步会去创建一个代理对象,这与刚刚缓存的bean对象就有了歧义,因为他俩的指向不是同一个对象了,刚刚给BService赋值的AService是一个普通对象,而真正进入单例池的AService是一个代理对象。
B依赖的A和最终的A不是同一个对象。
AOP就是通过一个BeanPostProcessor来实现的,Spring中的AOP是通过JDK或者Cglib来实现的,所以给一个类中的方法增加切面最终这个类创建后就会得到一个代理对象。

避免这种问题发生就利用到了三级缓存singletonFactories
先来看看三级缓存相关源码:

private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

这个是三级缓存的map定义,value是一个对象工厂

// 循环依赖-添加到三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

在bean的生命周期中, 生成完原始对象之后,就会构造一个ObjectFactory存入singletonFactories中。这个ObjectFactory 是一个函数式接口,所以支持Lambda表达式:() -> getEarlyBeanReference(beanName, mbd, bean)
上面的Lambda表达式就是一个ObjectFactory,执行该Lambda表达式就会去执行 getEarlyBeanReference方法,而该方法如下:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
   Object exposedObject = bean;
   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
         exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
      }
   }
   return exposedObject;
}

该方法会去执行SmartInstantiationAwareBeanPostProcessor中的getEarlyBeanReference方法, 而这个接口下的实现类中只有两个类实现了这个方法,一个是AbstractAutoProxyCreator,一个是 InstantiationAwareBeanPostProcessorAdapter,它的实现如下:

// InstantiationAwareBeanPostProcessorAdapter
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {   
    return bean; 
}
//AbstractAutoProxyCreator
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
   Object cacheKey = getCacheKey(bean.getClass(), beanName);
   this.earlyProxyReferences.put(cacheKey, bean);
   return wrapIfNecessary(bean, beanName, cacheKey);
}

getEarlyBeanReference方法的具体作用:首先得到一个cachekey,cachekey就是 beanName。 然后把beanName和bean(这是原始对象)存入earlyProxyReferences中 调用 wrapIfNecessary进行AOP,得到一个代理对象。

在引入这个三级缓存重新理解一下spring解决循环依赖的过程:
在创建AService的时候要去存入ObjectFactory的三级缓存,发现依赖BService,创建BService,依赖AService,从单例池中寻找发现为空,从二级缓存中寻找发现为空,从三级缓存中寻找存在,然后执行ObjectFactory也就是getEarlyBeanReference方法(如果有AOP则返回代理对象,无AOP就是普通对象)然后存入二级缓存,然后赋值给AService...
画图理解:

解决循环依赖 (1).png 其他问题:
正常Bean的创建发生AOP是在初始化之后去生成的代理对象
循环依赖创建Bean发生AOP是在赋值属性的时候由ObjectFactory去生成的代理对象,这里在初始化之后就不会再去创建一个代理对象了,这里是靠的earlyProxyReferences,在 AbstractAutoProxyCreator的postProcessAfterInitialization方法中,会去判断当前beanName是 否在earlyProxyReferences,如果在则表示已经提前进行过AOP了,无需再次进行AOP。