专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Spring源码分析-十、循环依赖的解决

getBean到doGetBean

==在了解Spring解决循环依赖的流程之前,建议先看下笔者之前写的AOP源码分析以及populateBean方法分析的文章==

在我们创建一个bean的时候, 是由getBean方法开始创建的, 该方法往下调, 最终会调到doGetBean方法, 而
doGetBean方法才可以算是开始创建bean了, 在调用的过程中, 会涉及到两个getSingleton方法, 为了在文章
中更好的显示它们, 笔者对这两个不同的重载方法利用geteSingleton1和getSingleton2进行区分, 同时我们
假设A类和B类形成了一个循环依赖, 并且A类开始创建

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

  final String beanName = transformedBeanName(name);
  Object bean;

  // Eagerly check singleton cache for manually registered singletons.
  Object sharedInstance = getSingleton1(beanName);
  if (sharedInstance != null && args == null) {
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  } else {
    .....
    if (mbd.isSingleton()) {
      sharedInstance = getSingleton2(beanName, () -> {
          return createBean(beanName, mbd, args);
      });
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
  }
}

protected Object getSingleton1(String beanName, boolean allowEarlyReference) {
  Object singletonObject = this.singletonObjects.get(beanName);
  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    synchronized (this.singletonObjects) {
      singletonObject = this.earlySingletonObjects.get(beanName);
      if (singletonObject == null && allowEarlyReference) {
        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
        if (singletonFactory != null) {
          singletonObject = singletonFactory.getObject();
          this.earlySingletonObjects.put(beanName, singletonObject);
          this.singletonFactories.remove(beanName);
        }
      }
    }
  }
  return singletonObject;
}

分析:
  以单例bean的创建为例子, 在doGetBean中, Spring会获取到bean的真正的beanName, 然后调用
  getSingleton1方法, 在getSingleton1方法中, Spring会从singletonObjects中取bean, 最终我们创建的
  bean都会放到这里来, 在创建一个A类实例的时候, 从缓存中自然是拿不到的, 此时singletonObject==null

  我们可以看到还要一个isSingletonCurrentlyInCreation方法被调用了, 大家可以点进去看, 其实SPring
  就是判断singletonsCurrentlyInCreation这个集合中是否存在当前beanName, 在上述流程中, 我们可以
  看到是没有往里面放入任何一个beanName的, 所以不会进入if语句块, 即本次调用getSingleton1返回的是null

  sharedInstance为null, 则Spring开始走getSinleton2来真正的创建A类实例

getSingleton2方法中调用createBean方法

Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
  beforeSingletonCreation(beanName);
  singletonObject = singletonFactory.getObject();
  afterSingletonCreation(beanName);
  addSingleton(beanName, singletonObject);
}
return singletonObject;

分析:
  beforeSingletonCreation方法中, 有行代码this.singletonsCurrentlyInCreation.add(beanName),
  可以看到, Spring此时才会将A类对应的beanName放到这个集合中, 然后调用singletonFactory.getObject
  方法创建bean, 其实就是调用createBean方法, bean对象创建完成后, afterSingletonCreation方法中才
  会将beanName从singletonsCurrentlyInCreation中移除, 需要注意的是!!!当A类实例还在创建时, 这个
  集合中是存在A类的beanName的, 换句话说, 如果再一次调用getSingleton1方法, 是能进入到那个if判断中

createBean方法最终调到了doCreateBean方法

createBean方法中调用了resolveBeforeInstantiation方法, 之后调用了doCreateBean方法, 之前我们在解
析AOP源码的时候已经对该方法进行了详细的描述, 里面完成AOP的初始化工作, 找出了所有的通知等

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {
  // Instantiate the bean.
  BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
  final Object bean = instanceWrapper.getWrappedInstance();
  Class<?> beanType = instanceWrapper.getWrappedClass();

  applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);

  // Eagerly cache singletons to be able to resolve circular references
  // even when triggered by lifecycle interfaces like BeanFactoryAware.
  boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
  if (earlySingletonExposure) {
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  }

  // Initialize the bean instance.
  Object exposedObject = bean;
  populateBean(beanName, mbd, instanceWrapper);
  exposedObject = initializeBean(beanName, exposedObject, mbd);

  return exposedObject;
}

分析:
  笔者将整个doCreateBean方法冗余的代码都删除了, 只留下了核心的代码, createBeanInstance方法完成了
  A类实例的创建, 此时bean对象被创建了, 该方法之前笔者也详细进行了分析, 从推断构造方法到构造方法的
  自动注入都进行了分析, 然后调用applyMergedBeanDefinitionPostProcessors方法完成了@Autowired等
  注解标注的属性的扫描, 将它们缓存到一个InjectionMetada中, 之后调用populateBean中取出一个个的
  InjectedElement, 从容器中利用getBean方法找到需要注入的属性, 最后调用initializeBean方法完成AOP
  以及一些初始化方法的调用(@PostConstruct)

  需要注意的是, 在调用populateBean方法的时候, 还处于A类的创建中, 其里面有一个属性B, 我们调用
  populateBean方法填充B, 其实就是调用getBean方法从容器中获取B, 此时B还没创建, 于是又回到了整篇
  文章的开头

  B类调用getSingleton1方法, 从singletonObjects这个map中找不到(因为还没创建), 而此时因为还没调用
  getSingleton2, 所以在singletonsCurrentlyInCreation这个集合中也没有B的beanName, 所以if判断不
  会进入, 之后调用getSingleton2, 此时会将B的beanName放入到singletonsCurrentlyInCreation集合中,
  然后调用createBean, 之后又到了doCreateBean方法中

  调用createBeanInstance方法创建B对象, 调用populateBean方法填充B对象中的属性即A类实例, 此时会调
  用getBean方法从容器中获取A类实例

  调用getSingleton1方法, 从singletonObjects这个map中找不到(因为A虽然处于创建过程中, 但是还没有
  一步是将A对象放入到这个map), 需要注意的是, 此时singletonsCurrentlyInCreation这个集合中已经存在
  了A类的beanName, 所以能够进入到if, 如下:
  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    singletonObject = this.earlySingletonObjects.get(beanName);
    if (singletonObject == null && allowEarlyReference) {
      ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
      if (singletonFactory != null) {
        singletonObject = singletonFactory.getObject();
        this.earlySingletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
      }
    }
  }

  可以看到, 先从earlySingletonObjects中获取对象, 我们此时是获取不到的, 因为我们没放入过, 但是, 
  this.singletonFactories.get(beanName)是能返回一个单例工厂的, 而这个单例工厂是我们创建A的时候,
  调用doCreateBean方法中调用的, 在createBeanInstance方法后, populateBean方法前, 有下面的代码:
  boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
  if (earlySingletonExposure) {
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  }

  protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }
    }

  跟进addSingletonFactory方法, 我们发现, 其实就是往singletonFactories放入了一个单例工厂, 而这个
  单例工厂只有一个方法, 调用这个单例工厂的方法就等于调用getEarlyBeanReference方法, 所以说当我们
  在上述getSingleton1中调用singletonFactory.getObject()方法的时候, 其实就是调用了这个
  getEarlyBeanReference方法获取A对象, 获取完成后放入到earlySingletonObjects中

  换句话说, 当循环依赖产生的时候, A类会先往singletonFactories中放入一个单例工厂对象, 这个叫做单例
  对象的二级缓存, 然后当B要填充属性时, 填充的对象为A对象时, 此时会调到getSingleton1中, 由于在
  singletonsCurrentlyInCreation这个集合中已经存在A了, 所以能够进if判断, 从而调用
  this.singletonFactories.get(beanName)方法获取到之前放入到singletonFactories那个单例工厂对象,
  从而调用单例工厂对象的getObject方法获取单例bean, 然后放入到earlySingletonObjects这个map中,
  这也叫三级缓存, 最后在整个A对象创建完成后, 才会放到singletonObjects中, 这叫一级缓存, 一级缓存
  是最终的缓存

  为什么需要二级缓存, 即为什么一开始要放到单例工厂中?
    我们点进getEarlyBeanReference方法看到, 其实里面就是调用了AOP对象的创建流程, 因为在循环依赖发
    生的时候, 我们真正创建AOP对象是在initializeBean方法, 而populateBean方法是在initializeBean
    方法之前创建的, 假设A最终会是一个代理对象, 如果没有二级缓存利用getEarlyBeanReference方法来创
    建代理对象, 那么我们注入到B的时候, 就会注入的是一个原始对象, 而不是一个代理对象

  为什么需要三级缓存, 而不是直接放入到singletonObjects中?
    对于这个, 笔者认为, 对于一个单例bean, 其放入真正的单例缓存应该是一个固定的位置, 即getSingleton2
    方法的最后, 即createBean方法之后, 而不是在其它地方, 所以这里采用一个三级缓存来进行过渡

文章永久链接:https://tech.souyunku.com/36511

未经允许不得转载:搜云库技术团队 » Spring源码分析-十、循环依赖的解决

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们