Spring源码解析-三级缓存与循环依赖

Posted java构架师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring源码解析-三级缓存与循环依赖相关的知识,希望对你有一定的参考价值。

前言

看Spring如何解决鸡生蛋,蛋生鸡问题

脉络

@Controller
public class A {
    // A注入B
    @Autowired
    private B b;
}
​
@Controller
public class B {
    // B注入A
    @Autowired
    private A a;
​
}

image.png

我们先从一个整体的视角看循环依赖是如何解决的

bean从一个bd变成一个正式的对象要经过实例化、属性赋值、初始化三个过程,实例化的时候对象只是一个空壳,属性没有值,这个时候Spring会把这个空壳暴露出来,放到缓存中。就是这个把空壳放到缓存中的操作解决循环依赖的问题。

如上图,A依赖了B,B依赖了A,将A变成bean的过程中,会通过createBeanInstance()方法创建A对象,populateBean()会对A对象进行属性赋值,在这两个方法中间,有一个非常重要方法addSingletonFactory(),此方法会将实例好的A空壳放到缓存中,对A进行属性赋值的过程中,发现B还未存在于IOC容器中,会执行getBean(B)方法,B依赖了A,在对B进行属性赋值的时候,就会从缓存中拿到A空壳,这样子B就能顺利变成一个bean,A对象属性赋值的问题自然也就解决了

源码解析

三级缓存其实就是三个容器,里面存放了bean生命周期的不同过程

  /** Cache of singleton objects: bean name to bean instance. */
  // 一级缓存,存放的是实例化和初始化都完成的完整对象
  private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
​
  /** Cache of singleton factories: bean name to ObjectFactory. */
  // 三级缓存,存放的是实例化完成但初始化未完成的对象,特别注意的是,这里存放value是一个匿名内部类
  private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
​
  /** Cache of early singleton objects: bean name to bean instance. */
  // 二级缓存,存放的是实例化完成但初始化未完成的对象
  private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

在看下面的源码之前,我们需要带着两个问题?

  1. 出现了循环依赖,Spring的流程是怎么样的?
  2. 没有出现循环依赖,Spring的流程是怎么样的?

两个流程理论上是互不影响的

protected <T> T doGetBean(
      String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
      throws BeansException {
    // bean名称转换
    // 根据指定的名称获取被管理bean的名称
    // 如果指定是别名,将别名转换为规范的Bean名称
    String beanName = transformedBeanName(name);
    Object bean;
​
    // 从各级缓存中获取bean对象
    Object sharedInstance = getSingleton(beanName);
    // 这个判断是跟factoryBean相关的
    if (sharedInstance != null && args == null) {
      if (logger.isTraceEnabled()) {
        if (isSingletonCurrentlyInCreation(beanName)) {
          logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
              "' that is not fully initialized yet - a consequence of a circular reference");
        }
        else {
          logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
        }
      }
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
​
    ......
​
        // 创建bean对象
        if (mbd.isSingleton()) {
          sharedInstance = getSingleton(beanName, () -> {
            try {
              return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
              // Explicitly remove instance from singleton cache: It might have been put there
              // eagerly by the creation process, to allow for circular reference resolution.
              // Also remove any beans that received a temporary reference to the bean.
              destroySingleton(beanName);
              throw ex;
            }
          });
          bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
        }
​
        ......
    // 返回bean对象
    return (T) bean;
  }

我们重点看两个getSingleton()方法

// 这段代码三级缓存容器都涉及到了,初看有点懵,但是思路不难,先从一级缓存找,找不到去二级缓存找,还找不到去三级缓存
// 如果在三级缓存找到了,就放到二级缓存中,非常像数据库跟redis的缓存策略,先到redis中找,找不到去数据库找,找到再写到redis中
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 先从一级缓存中找bean
    Object singletonObject = this.singletonObjects.get(beanName);
    // 这里有两个判断,singletonObject == null 表示在一级缓存中找不到bean
    // isSingletonCurrentlyInCreation()表示该bean是否在创建中
    // 如果两个条件满足,说明出现了循环依赖
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
        // 再从二级缓存中获取
        singletonObject = this.earlySingletonObjects.get(beanName);
        // 这里又有两个判断
        // singletonObject == null 表示在二级缓存中没有获取到
        // allowEarlyReference 这个值传进来就为true,表示是否允许获取早期引用
        // 如果两个条件都满足,就到三级缓存中获取
        if (singletonObject == null && allowEarlyReference) {
          // 从三级缓存获取,注意,这里获取到是一个ObjectFactory
          ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
          if (singletonFactory != null) {
            // 通过ObjectFactory获取bean实例
            singletonObject = singletonFactory.getObject();
            // 打怪升级,从三级缓存升级到二级缓存,
            this.earlySingletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
          }
        }
      }
    }
    // 返回bean
    return singletonObject;
  }
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    synchronized (this.singletonObjects) {
      // 从一级缓存中获取bean
      Object singletonObject = this.singletonObjects.get(beanName);
      // 如果bean为空
      if (singletonObject == null) {
        if (this.singletonsCurrentlyInDestruction) {
          throw new BeanCreationNotAllowedException(beanName,
              "Singleton bean creation not allowed while singletons of this factory are in destruction " +
              "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
        }
        if (logger.isDebugEnabled()) {
          logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
        }
        // 单例bean创建前会将这个bean放到一个容器中,标志这个bean正在创建中
        beforeSingletonCreation(beanName);
        boolean newSingleton = false;
        boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
        if (recordSuppressedExceptions) {
          this.suppressedExceptions = new LinkedHashSet<>();
        }
        try {
          // 执行匿名内部类方法
          singletonObject = singletonFactory.getObject();
          newSingleton = true;
        }
        catch (IllegalStateException ex) {
          singletonObject = this.singletonObjects.get(beanName);
          if (singletonObject == null) {
            throw ex;
          }
        }
        catch (BeanCreationException ex) {
          if (recordSuppressedExceptions) {
            for (Exception suppressedException : this.suppressedExceptions) {
              ex.addRelatedCause(suppressedException);
            }
          }
          throw ex;
        }
        finally {
          if (recordSuppressedExceptions) {
            this.suppressedExceptions = null;
          }
          // 单例bean创建完成后,会把bean从这个容器中移除
          afterSingletonCreation(beanName);
        }
        // newSingleton为true时,表示bean创建成功
        if (newSingleton) {
          // 把bean放到一级缓存中,一级缓存就是我们常说的IOC容器
          addSingleton(beanName, singletonObject);
        }
      }
      return singletonObject;
    }
  }
protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
      // 放进一级缓存中
      this.singletonObjects.put(beanName, singletonObject);
      // 从三级缓存中移除
      this.singletonFactories.remove(beanName);
      // 从二级缓存中移除
      this.earlySingletonObjects.remove(beanName);
      this.registeredSingletons.add(beanName);
    }
}

从上面的代码我们已经可以总结出bean没有出现循环依赖时,流程是怎么样的?

  1. getSingleton()方法查看是否存在于一级缓存中,是则获取,无则走下面的创建流程,因为没有出现循环依赖,isSingletonCurrentlyInCreation(beanName),此判断是false
  2. createBean()方法创建一个bean
  3. addSingleton()方法将bean放到一级缓存中

当然,在正常的流程中会穿插异常流程的处理方式,比如bean创建前会把bean放到一个容器中,Spring凭借这个可以知道bean是否正在创建中,得知bean在创建中后,会利用三级缓存获得bean空壳

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {
​
    ....
​
    // mbd.isSingleton():bean是否是单例
    // this.allowCircularReferences:是否允许出现循环依赖
    // isSingletonCurrentlyInCreation(beanName):bean是否在创建中
    // 如果三个条件都满足,说明出现了循环依赖
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
      if (logger.isTraceEnabled()) {
        logger.trace("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
      }
      // 将bean放到三级缓存中
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
​
    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
      // 属性赋值
      populateBean(beanName, mbd, instanceWrapper);
      // bean初始化
      exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    catch (Throwable ex) {
      if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
        throw (BeanCreationException) ex;
      }
      else {
        throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
      }
    }
    // 如果出现了循环依赖
    if (earlySingletonExposure) {
      // 从各级缓存中获取bean,getSingleton(beanName, false)这个方法是不是很熟悉,上面已经使用到了
      // 只是第二个参数从true变成了false,这个参数决定了要不要从第三级缓存中获取数据
      // 代码走到这个地方,已经无需获取早期引用了,所以参数为false
      Object earlySingletonReference = getSingleton(beanName, false);
      // 如果二级缓存不为空
      if (earlySingletonReference != null) {
        // 并且二级缓存中的bean跟exposedObject相等,说明初始化时,没有使用代理修改bean对象
        if (exposedObject == bean) {
          // 直接将二级缓存中bean返回即可
          exposedObject = earlySingletonReference;
        }
    ....
​
    return exposedObject;
  }

我们重点看看addSingletonFactory()这个方法,我们先看一段简单代码,感受一下ObjectFactory这个类的作用

public class ObjectFactoryTest {
​
​
    public static void main(String[] args) {
        HashMap<String, ObjectFactory<?>> map = new HashMap<>(2);
        map.put("a", () -> {
            System.out.println("objectFactory");
            return "string";
        });
​
        System.out.println(map.get("a").getObject());
    }
}

image.png 原来,ObjectFactory可以定制执行方法,那么Spring要定制的执行方法是什么?

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
      if (!this.singletonObjects.containsKey(beanName)) {
        // 放入三级缓存
        this.singletonFactories.put(beanName, singletonFactory);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
      }
    }
}
// 要定制的执行方法就是一个beanPostProcess的埋点
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
          SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
          exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
        }
      }
    }
    return exposedObject;
}
// AOP的beanPostProcess重写了这个方法
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    return wrapIfNecessary(bean, beanName, cacheKey);
}

原来,要定制执行方法的原因是,bean的初始化的过程中,AOP动态代理有可能对这个bean进行增强,增强后的bean就是一个代理类,不是原来的类,循环依赖时,我们要提前暴露bean,但是动态代理的过程也要走,鱼和熊掌都要,就使用了这种比较骚气的方式

从上面的代码中,我们可以总结出出现循环依赖时,流程是怎么样的?

  1. addSingletonFactory()方法会将实例化好的空壳对象放在三级缓存中
  2. getSingleton()方法查看到出现循环依赖,会从三级缓存中获取bean,执行定制方法,如果是简单对象,则将简单对象放进二级缓存中,如果是动态代理对象,则将动态代理对象放进二级缓存中
  3. bean生命周期走完后,会把bean放进一级缓存中

测试一下

@Controller
public class A {
​
    @Autowired
    private B b;
​
​
    public void test(){
        System.out.println("a");
    }
}
@Controller
public class B {
​
    @Autowired
    private A a;
​
    public void test(){
        System.out.println("b");
    }
​
}

定义一个切面,如果有这个测试类,可以看到调用动态代理方法的过程

@Aspect
@Component
public class AspectDemo {
​
    @Pointcut("execution(* *.test(..))")
    public void test(){}
​
    @Before("test()")
    public void before(){
        System.out.println("before test..");
    }
​
    @After("test()")
    public void after(){
        System.out.println("after test..");
    }
​
    @Around("test()")
    public Object around(ProceedingJoinPoint p){
        System.out.println("before");
        Object o = null;
        try {
            o = p.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("after");
        return o;
    }
}
@Configuration
@ComponentScan("com.example.springdemo.circle")
@EnableAspectJAutoProxy
public class CircleConfig {
}
public class CircleTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(CircleConfig.class);
        applicationContext.refresh();
​
        A a = (A) applicationContext.getBean(A.class);
        a.test();
        applicationContext.close();
    }
}

问题

一级缓存能不能解决循环依赖问题?

不能,bean生命周期过程中bean有多种形态,有实例化完成,初始化未完成,也有实例化初始化都完成,这些形态都放在同一个缓存中,使用者获取时无法区分该bean目前处于什么形态

二级缓存能不能解决问题?为什么非要用三级缓存?

如果A类上配置AOP,就需要生成一个代理对象,需要三级缓存的最主要意义在于,你所需要的类有可能是简单对象(实例化,初始化),也可能是需要进行代理的代理对象,当我向三级缓存中放置匿名内部类的生活,可以在获取的时候决定到底是简单对象,还是代理对象。

特别注意的是:并不是所有对象都需要从三级缓存到二级缓存再到一级缓存,有的对象可以直接从三级缓存到一级缓存。


 

以上是关于Spring源码解析-三级缓存与循环依赖的主要内容,如果未能解决你的问题,请参考以下文章

0源码基础学习Spring源码系列——Spring如何解决循环依赖

手撕Spring源码,详细理解Spring循环依赖及三级缓存

Spring源码分析系列-循环依赖和三级缓存

Spring源码分析系列-循环依赖和三级缓存

Spring源码分析系列-循环依赖和三级缓存

Spring源码分析系列-循环依赖和三级缓存