《Yangzai的知识地图》- Spring循环依赖

╰+哭是因爲堅強的太久メ 2022-10-29 10:23 165阅读 0赞

Spring如何解决循环依赖

系类文章
1、Spring源码分析
2、Spring开篇
3、Bean的生命周期


文章目录

  • Spring如何解决循环依赖
  • 前言
  • 一、什么是循环依赖?
    • 0.Spring容器启动时初始化Bean是哪种
    • 1.构造函数的循环依赖
    • 2.Setter和属性注入的循环依赖
  • 二、为什么使用三级缓存
    • 0.三级缓存都是什么
    • 1.两级缓存
    • 2.为什么使用三级缓存
      • 2.1 后置处理可能会产生代理

前言

这是个人技术学习总结的相关文章,是对自己的查缺补漏。


一、什么是循环依赖?

简单的说就是A类中拥有B类的成员变量,而B类中也拥有A类的成员变量,由于Spring的机制是所有的Bean都交给容器进行管理,如果没有解决循环以来就会出现这样的问题,在创建A的时候 因为A拥有B类的成员变量所以在属性填充的时候就会去创建B类,而在创建B类的时候又由于B类中拥有A类的成员变量,所以会想要创建A类的成员变量,如果这个时候不想办法解决,那么就是一致依赖创建下去。而Spring循环依赖机制就是来解决这个的。

0.Spring容器启动时初始化Bean是哪种

Spring容器在初始化容器的时候只会对所有的单例非懒加载的实例进行初始化。

1.构造函数的循环依赖

首先Spring是解决不了构造函数的循环依赖的问题的,这是因为Spring解决循环依赖是通过三级缓存的方式来达成的,而所谓的第二级缓存存放的是实例化而未进行属性填充和初始化的不完整Bean实例,通过提前暴露ObjectFactory对象到三级缓存中来进行解决。而构造函数的循环依赖需要使用所依赖的对象来实例化自己,而这时是拿不到所依赖的对象的实例化后的不完整Bean,除非我们将这个Bean设置为懒加载,这样在进行构造函数注入的时候就可以避免循环依赖的问题了。【因为懒加载的Bean是不需要实例化的只有在用的时候才会进行相关操作】

2.Setter和属性注入的循环依赖

Setter和属性注入类似,我们就拿属性注入来说比如A依赖B ,B依赖A。

  1. 当创建A的时候首先会对A去一级缓存里面找,当然了第一次是找不到的。
  2. 在一级缓存找不到A ,就会设置A为创建状态,然后创建A
  3. getSingleton(String beanName, ObjectFactory<?> singletonFactory) 是用来创建A对象的,通过传入的lamda表达式进行创建
  4. 首先会先实例化A对象 ,实例化之后会调用addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));将A放入三级缓存

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {

    1. Assert.notNull(singletonFactory, "Singleton factory must not be null");
    2. synchronized (this.singletonObjects) {
    3. if (!this.singletonObjects.containsKey(beanName)) {
    4. this.singletonFactories.put(beanName, singletonFactory);
    5. this.earlySingletonObjects.remove(beanName);
    6. this.registeredSingletons.add(beanName);
    7. }
    8. }
    9. }
  5. 然后对A进行属性填充,因为B是A的成员变量所以会进行填充

    if (pvs != null) {

    1. applyPropertyValues(beanName, mbd, bw, pvs);
    2. }
  6. 上面的方法会去调用工厂类的getBean方法去创建B

    if (value instanceof RuntimeBeanReference) {

    1. RuntimeBeanReference ref = (RuntimeBeanReference) value;
    2. return resolveReference(argName, ref);
    3. }
  7. 同样的B也会将自己及一个lamda表达式放入三级缓存内,

  8. 此时进行到B的属性填充B要去获取A【就像上面5那样】同样的由于6部分的处理因为A也是RuntimeBeanReference所以回去调用A的创建而这时在1处我们一开始是获取不到A的,但是现在

    @Nullable

    1. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    2. //因为A还没有完成初始化所以一级缓存里没有A的Bean实例
    3. Object singletonObject = this.singletonObjects.get(beanName);
    4.   //因为singletonObject 为null并且2的位置我们已经设置了A的创建状态所以可以进入到内层
    5. if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    6. synchronized (this.singletonObjects) {
    7. //我们放入二级缓存的操作所以此处为null,可以进入下一层
    8. singletonObject = this.earlySingletonObjects.get(beanName);
    9. if (singletonObject == null && allowEarlyReference) {
    10. //这个地方就是从三级缓存里获取我们4处放入的lamda表达式,因为已经放入所以不为null,可以进入下一层
    11. ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    12. if (singletonFactory != null) {
    13. //这块就是执行我们放入的lamda方法,可以见第9处的说明,该方法返回的可能是Bean本身或代理对象
    14. singletonObject = singletonFactory.getObject();
    15. //将A放入二级缓存
    16. this.earlySingletonObjects.put(beanName, singletonObject);
    17. //删除A的三级缓存
    18. this.singletonFactories.remove(beanName);
    19. }
    20. }
    21. }
    22. }
    23. return singletonObject;
    24. }
  9. 先看一下三级缓存中匿名类的方法体

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {

    1. Object exposedObject = bean;
    2. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    3. for (BeanPostProcessor bp : getBeanPostProcessors()) {
    4. if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
    5. SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
    6. //这个位置的方法跟下去回进行代理
    7. exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
    8. }
    9. }
    10. }
    11. return exposedObject;
    12. }
  10. 因为从三级缓存中找到了A所以通过bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);返回了二级缓存中的A【这是一个不完整的Bean】

  11. 然后继续进行B类的初始化然后B初始化完成也就是说B实例创建完了此时回到

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {

    1. Assert.notNull(beanName, "Bean name must not be null");
    2. synchronized (this.singletonObjects) {
    3. Object singletonObject = this.singletonObjects.get(beanName);
    4. if (singletonObject == null) {
    5. if (this.singletonsCurrentlyInDestruction) {
    6. throw new BeanCreationNotAllowedException(beanName,
    7. "Singleton bean creation not allowed while singletons of this factory are in destruction " +
    8. "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
    9. }
    10. if (logger.isDebugEnabled()) {
    11. logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
    12. }
    13. beforeSingletonCreation(beanName);
    14. boolean newSingleton = false;
    15. boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
    16. if (recordSuppressedExceptions) {
    17. this.suppressedExceptions = new LinkedHashSet<>();
    18. }
    19. try {
    20. //此时B实例获取完毕
    21. singletonObject = singletonFactory.getObject();
    22. newSingleton = true;
    23. }
    24. catch (IllegalStateException ex) {
    25. // Has the singleton object implicitly appeared in the meantime ->
    26. // if yes, proceed with it since the exception indicates that state.
    27. singletonObject = this.singletonObjects.get(beanName);
    28. if (singletonObject == null) {
    29. throw ex;
    30. }
    31. }
    32. catch (BeanCreationException ex) {
    33. if (recordSuppressedExceptions) {
    34. for (Exception suppressedException : this.suppressedExceptions) {
    35. ex.addRelatedCause(suppressedException);
    36. }
    37. }
    38. throw ex;
    39. }
    40. finally {
    41. if (recordSuppressedExceptions) {
    42. this.suppressedExceptions = null;
    43. }
    44. afterSingletonCreation(beanName);
    45. }
    46. if (newSingleton) {
    47. //将B 的 Bean放入单例池
    48. addSingleton(beanName, singletonObject);
    49. }
    50. }
    51. return singletonObject;
    52. }
    53. }
  1. protected void addSingleton(String beanName, Object singletonObject) {
  2. synchronized (this.singletonObjects) {
  3. //将B放入一级缓存
  4. this.singletonObjects.put(beanName, singletonObject);
  5. //删除B在二级缓存的内容,其实这个位置并没有存在B,因为并没有像A一样的触发操作
  6. this.singletonFactories.remove(beanName);
  7. //删除B 在三级缓存中的值
  8. this.earlySingletonObjects.remove(beanName);
  9. this.registeredSingletons.add(beanName);
  10. }
  11. }
  1. 这时A的属性填充已经完毕 ,开始进行A的初始化A初始化完成后,会像11处的那样将A放入一级缓存然后删除A的二级缓存。
  2. 这样就解决了AB的循环依赖问题

二、为什么使用三级缓存

0.三级缓存都是什么

  1. public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
  2. /** Cache of singleton objects: bean name to bean instance. */
  3. //单例对象缓存,beanName-Bean实例 【一级缓存】
  4. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  5. /** Cache of singleton factories: bean name to ObjectFactory. */
  6. //单例工厂缓存,beanName-ObjectFactory【三级缓存】
  7. private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  8. /** Cache of early singleton objects: bean name to bean instance. */
  9. //初期的单例对象缓存【也就是只进行了实例化而未初始化的】这个是二级缓存
  10. private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
  11. }

1.两级缓存

如果只有两级缓存也就是没有singletonFactories ,那么其实对于普通的bean【所谓的普通的bean其实就是不需要进行代理的bean对象】两级缓存本身已经解决了循环依赖。

其实只需要singletonObjects 存放已经初始化完全的单例对象
earlySingletonObjects 存放未初始化完全的对象,当我们需要提前暴露依赖对象的时候就放在earlySingletonObjects 里就可以,如果初始化完全了就丢在singletonObjects这里,这样就可以解决循环依赖

2.为什么使用三级缓存

想要明白这个地方首先要知道Bean的生命周期具体可以参考Bean的生命周期

2.1 后置处理可能会产生代理

我们知道一个bean的生命周期主要是包括bean由xml或注解进行解析为Bean Definition然后注册到容器,之后就是实例化、属性填充、初始化,对于初始化之后如果实现了BeanPostProcessor接口则会执行postProcessAfterInitialization方法
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
这个类的

  1. @Override
  2. public Object postProcessAfterInitialization(@Nullable Object >bean, String beanName) {
  3. if (bean != null) {
  4. Object cacheKey = getCacheKey(bean.getClass(), >beanName);
  5. //注意这个earlyProxyReferences
  6. //这块的逻辑就是如果早期代理引用缓存中有直接返回原始的实例化bean'【注意不是代理对象】没有的话或者不想等则正常进行代理
  7. if (this.earlyProxyReferences.remove(cacheKey) != bean) {
  8. return wrapIfNecessary(bean, beanName, cacheKey);
  9. }
  10. }
  11. return bean;
  12. }

我们回想一下1.2.9 也就是 一部分中的第九步,将A放入三级缓存的时候我们是将A对象和一个匿名内部类放在了三级缓存中,我们现在在会看一下这个lamda表达式

  1. // Eagerly cache singletons to be able to resolve circular references
  2. // even when triggered by lifecycle interfaces like BeanFactoryAware.
  3. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
  4. isSingletonCurrentlyInCreation(beanName));
  5. if (earlySingletonExposure) {
  6. if (logger.isTraceEnabled()) {
  7. logger.trace("Eagerly caching bean '" + beanName +
  8. "' to allow for resolving potential circular references");
  9. }
  10. addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  11. }
  12. protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
  13. Object exposedObject = bean;
  14. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
  15. for (BeanPostProcessor bp : getBeanPostProcessors()) {
  16. if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
  17. SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
  18. //我们跟进去这个方法
  19. exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
  20. }
  21. }
  22. }
  23. return exposedObject;
  24. }
  25. default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
  26. return bean;
  27. }
  • 这个方法有三个实现类 但是只有一个是有用的就是

    @Override

    1. public Object getEarlyBeanReference(Object bean, String beanName) {
    2. Object cacheKey = getCacheKey(bean.getClass(), beanName);
    3. //就是在这个地方put的这样就避免了被代理两次的尴尬
    4. this.earlyProxyReferences.put(cacheKey, bean);
    5. return wrapIfNecessary(bean, beanName, cacheKey);
    6. }

总之就是三级缓存是为了bean后置处理器部分代理时的问题产生的,如果没有三级代理,只有两级,那二级缓存放的时实例化后的对象,这样在执行后置处理的时候就会出现覆盖,而使用了三级缓存后,就保证了只有一个代理对象。


发表评论

表情:
评论列表 (有 0 条评论,165人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Spring循环依赖

    在Spring 中循环依赖是一个问题, 为什么? 因为,在Spring中,一个对象并不是简单new出来的, 而是会经过一系列的Bean的生命周期,就是因为Bean的生命周期,

    相关 Spring循环依赖

    什么是循环依赖? 循环依赖其实就是循环引用,也就是两个或者两个以上的 bean 互相持有对方,最终形成闭环。比如 A 依赖于 B,B 依赖于 C,C 又依赖于 A。如下图