Spring Boot进阶:原理、实战与面试题分析
上QQ阅读APP看书,第一时间看更新

2.3.1 三级缓存结构

所谓的三级缓存,在Spring中表现为三个Map对象,如代码清单2-17所示。这三个Map对象定义在DefaultSingletonBeanRegistry类中,该类是DefaultListableBeanFactory的父类。

代码清单2-17 DefaultSingletonBeanRegistry中的三级缓存Map定义代码

/** 单例对象的缓存: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** 单例对象工厂的缓存: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** 提前暴露的单例对象的缓存: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

请注意,这里的singletonObjects变量就是第一级缓存,用来持有完整的Bean实例。而earlySingletonObjects中存放的是那些提前暴露的对象,也就是已经创建但还没有完成属性注入的对象,属于第二级缓存。最后的singletonFactories存放用来创建earlySingleton-Objects的工厂对象,属于第三级缓存。

那么,三级缓存是如何发挥作用的呢?让我们来分析获取Bean的代码流程,如代码清单2-18所示。

代码清单2-18 获取Bean的getSingleton()方法代码

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //首先从一级缓存singletonObjects中获取
    Object singletonObject = this.singletonObjects.get(beanName);

    //如果获取不到,就从二级缓存earlySingletonObjects中获取
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);

            //如果还是获取不到,就从三级缓存singletonFactory中获取
            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;
}

看了这段代码,我们不难理解对三级缓存的依次访问过程,但可能还是不理解Spring为什么要这样设计。事实上,解决循环依赖的关键还是要围绕Bean的生命周期。在2.2.2节中介绍Bean的实例化时,我们知道它包含三个核心步骤,而在第一步和第二步之间,存在一个addSingletonFactory()方法,如代码清单2-19所示。

代码清单2-19 Bean实例化过程中的addSingletonFactory()方法代码

//1. 初始化Bean,通过构造函数创建Bean
instanceWrapper = createBeanInstance(beanName, mbd, args);

//针对循环依赖问题暴露单例工厂类
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

//2. 初始化Bean实例,完成Bean实例的完整创建
populateBean(beanName, mbd, instanceWrapper);

Spring解决循环依赖的诀窍就在于singletonFactories这个第三级缓存,上述addSingleton-Factory()方法用于初始化这个第三级缓存中的数据,如代码清单2-20所示。

代码清单2-20 addSingletonFactory()方法代码

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            //添加Bean到第三级缓存中
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

请注意,这段代码的执行时机是在已经通过构造函数创建Bean,但还没有完成对Bean中完整属性的注入的时候。换句话说,Bean已经可以被暴露出来进行识别了,但还不能正常使用。接下来我们就来分析一下为什么通过这种机制就能解决循环依赖问题。