天天看点

Java面试经典问题,一文读懂,Spring的Bean是如何实现三级缓存的

作者:Code404

在 Spring Boot 中,加载 Bean 时采用了三级缓存的概念,主要是依靠 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry 中的三个 Map 实现,即 singletonObjects、earlySingletonObjects 和 singletonFactories 缓存。

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
		......
    /** 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. */
    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 ConcurrentHashMap<>(16);
    ......
}           

1、singletonObjects缓存

singletonObjects 缓存是 Spring Boot 中默认的单例缓存,用于保存已经创建好的单例对象。当请求获取一个 Bean 时,Spring Boot 首先会去 singletonObjects 缓存中查找,如果能够找到就直接返回该对象,否则继续执行后续逻辑。

/** Cache of singleton objects: bean name to bean instance. */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);           

2、earlySingletonObjects缓存

如果singletonObjects缓存中不存在所请求的 Bean 对象,那么 Spring Boot 会去 earlySingletonObjects 缓存中查找。这个缓存用于保存已经创建但是还未填充属性的对象实例,也就是处于早期创建状态的 Bean 对象。

/** Cache of early singleton objects: bean name to bean instance. */
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);           

当 Bean 对象被创建时,先创建一个空对象并保存到 earlySingletonObjects 缓存中,接着开始填充属性。在填充属性的过程中,如果遇到循环依赖(即A依赖B,B依赖A),那么Spring Boot 会从 earlySingletonObjects 缓存中获取未填充属性的对象实例并将其返回,从而打破循环依赖。

3、singletonFactories缓存

如果即没有在 singletonObjects 缓存中找到所请求的 Bean 对象,也没有在 earlySingletonObjects 缓存中找到早期创建的Bean对象,那么 Spring Boot 会去 singletonFactories 缓存中查找。singletonFactories 缓存用于保存实例化 Bean 时产生的工厂对象。

/** Cache of singleton factories: bean name to ObjectFactory. */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);           

当第一次请求获取一个Bean时,Spring Boot会调用factory.getBean()创建工厂对象并将其保存到singletonFactories缓存中,同时调用getObjectFromFactoryBean()方法从工厂对象中获取实例化的Bean对象。在获取到Bean对象后,Spring Boot会将其保存到singletonObjects缓存中供后续使用。

DefaultSingletonBeanRegistry 的 getSingleton() 方法源码讲解

Spring 是通过 getSingleton() 方法来实现三级缓存获取 Bean 实例的,具体源码如下:

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 1、先从 singletonObjects 缓存中查找 Bean 实例,
    // 如果能够找到就直接返回该对象
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 2、如果 singletonObjects 缓存中不存在所请求的单例对象,
        // 再尝试从 earlySingletonObjects 缓存中获取对象。
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            // 3、如果 earlySingletonObjects 缓存中也没有找到,且允许早期引用,
            // 就加锁防止其它线程修改了该单例对象并创建新的 Bean 实例。
            synchronized (this.singletonObjects) {
                // 4、再次从 singletonObjects 缓存中获取实例对象
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    // 5、如果 singletonObjects 缓存中还是没有,
                    // 再从 earlySingletonObjects 缓存中查找
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        // 6、最后尝试从 singletonFactories 缓存中获取工厂对象,
                        // 并通过 getObject() 方法创建 Bean 实例对象。
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                            // 7、将早期创建的 Bean 对象加入到 earlySingletonObjects 缓存中
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            // 8、从 singletonFactories 缓存中移除该工厂对象
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}           

在获取单例 Bean 对象时,Spring Boot 会先从 singletonObjects 缓存中查找对象。

如果在该缓存中没有找到 Bean 实例,就会继续从 earlySingletonObjects 和 singletonFactories 缓存中查找。

如果允许早期引用,且在 earlySingletonObjects 缓存中也没有找到,Spring Boot 就会加锁,防止其它线程修改了该单例对象并创建新的 Bean 实例。

在获得到锁之后,又重复上面的步骤,从三个缓存Map中获取对象。

如果还是没有找到,那么 Spring Boot 就会调用工厂对象的 getObject() 方法创建新的实例,并将其保存到 earlySingletonObjects 缓存中,直到初始化完毕后将 Bean 对象保存到 singletonObjects 缓存中,供后续使用。

这就是三个缓存获取 Bean 实例的完整源码过程。

早期引用

上面的 getSingleton() 方法有一个参数 allowEarlyReference (允许早期引用),那么什么是早期引用呢?

如果能够在三级缓存过程的生命周期中使用这个早期对象,就称为早期引用。

早期引用的主要应用场景是解决循环依赖问题,当 A 对象依赖 B 对象,而 B 对象又依赖 A 对象时,需要先创建一个 A 对象实例的原始实例(即提前注入一些属性值但还未完全初始化),然后将其保存到早期缓存中,即 earlySingletonObjects。紧接着,再去创建 B 对象的实例并注入其所需要的属性值,然后完成 B 对象的初始化,最后再去完成 A 对象的初始化。

DefaultSingletonBeanRegistry 的 Bean 注册源码讲解

Spring 可以通过 registerSingleton() 、addSingleton()、addSingletonFactory() 三个方法,来实现注册 Bean 实例和 Bean 的工厂对象。

@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
    Assert.notNull(beanName, "Bean name must not be null");
    Assert.notNull(singletonObject, "Singleton object must not be null");
    synchronized (this.singletonObjects) {
        // 在singletonObjects缓存中查找对象oldObject
        Object oldObject = this.singletonObjects.get(beanName);
        if (oldObject != null) {
            // 如果已经存在对象,则抛出IllegalStateException异常,表示无法继续注册
            throw new IllegalStateException("Could not register object [" + singletonObject +
                    "] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
        }
        // 否则,调用addSingleton(beanName, singletonObject)方法实现注册操作。
        addSingleton(beanName, singletonObject);
    }
}

// 添加单例对象实例
protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        // 将 singletonObject 缓存到 singletonObjects 缓存中
        this.singletonObjects.put(beanName, singletonObject);
        // 从 singletonFactories 缓存中移除当前对象的工厂
        this.singletonFactories.remove(beanName);
        // 从 earlySingletonObjects 缓存中移除早期的缓存值
        this.earlySingletonObjects.remove(beanName);
        // 将注册成功的 Bean 名称添加到 registeredSingletons 集合中
        this.registeredSingletons.add(beanName);
    }
}

// 添加单例对象的工厂对象
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        // 如果该 Bean 名称没有对应的 Bean 对象,则缓存该工厂对象到 singletonFactories 中
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            // 从 earlySingletonObjects 缓存中移除对应的值
            this.earlySingletonObjects.remove(beanName);
            // 将注册成功的 Bean 名称添加到 registeredSingletons 集合中
            this.registeredSingletons.add(beanName);
        }
    }
}           

SpringBoot 在启动过程中是如何实现三级缓存

SpringBoot 是通过 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory,这个抽象工厂类,来实现创建三级缓存的过程。AbstractAutowireCapableBeanFactory 是继承上面讲到的 DefaultSingletonBeanRegistry 的子类。它的 doCreateBean() 用来创建 Bean,并且实现三级缓存的过程。具体源码如下:

// 根据传入的 beanName 和 RootBeanDefinition,
// 创建一个 Bean 实例并返回该实例对象
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {

    // 用于保存实例化后的 BeanWrapper 对象
    BeanWrapper instanceWrapper = null;

    // 如果该 Bean 是单例模式,
    // 则从 factoryBeanInstanceCache 中移除该 Bean 对应的 BeanWrapper
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }

    // 如果 BeanWrapper 为空,则通过 createBeanInstance() 方法创建 Bean 实例对象
    if (instanceWrapper == null) {
        // createBeanInstance
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }

    // 从 BeanWrapper 中获取 Bean 实例对象及其类型,并将其保存到 RootBeanDefinition 对象中
    Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();
    if (beanType != NullBean.class) {
        mbd.resolvedTargetType = beanType;
    }

    // 调用 MergedBeanDefinitionPostProcessor 的 postProcessMergedBeanDefinition() 方法对合并后的 BeanDefinition 进行额外的处理
    synchronized (mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            try {
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Post-processing of merged bean definition failed", ex);
            }
            mbd.postProcessed = true;
        }
    }

    // 如果是单例模式,则检查是否设置了 allowCircularReferences 属性,并且是否存在循环依赖关系
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
            isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        // 如果该 Bean 需要被提前暴露出来(可能会被其他 Bean 循环依赖),则将其加入到单例缓存中
        if (logger.isTraceEnabled()) {
            logger.trace("Eagerly caching bean '" + beanName + 
                "' to allow for resolving potential circular references");
        }
        // 注册 Bean 的工厂对象
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // 对 Bean 实例对象进行属性填充
    Object exposedObject = bean;
    try {
        // 调用 populateBean方法填充 Bean 的属性
        populateBean(beanName, mbd, instanceWrapper);
        // 调用initializeBean方法初始化 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);
        }
    }

    // 如果该 Bean 需要被提前暴露出来,则将它从单例缓存中移除
    if (earlySingletonExposure) {
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            }
            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                String[] dependentBeans = getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                for (String dependentBean : dependentBeans) {
                    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                        actualDependentBeans.add(dependentBean);
                    }
                }
                if (!actualDependentBeans.isEmpty()) {
                    throw new BeanCurrentlyInCreationException(beanName,
                            "Bean with name '" + beanName + "' has been injected into other beans [" +
                            StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                            "] in its raw version as part of a circular reference, but has eventually been " +
                            "wrapped. This means that said other beans do not use the final version of the " +
                            "bean. This is often the result of over-eager type matching - consider using " +
                            "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
                }
            }
        }
    }

    // 在 Bean 销毁时注册该 Bean 为可销毁 Bean
    try {
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }

    // 返回被暴露出去的 Bean 实例对象
    return exposedObject;
}           

缓存过程的三个步骤

从上面的源码中可以看出来,Spring Boot 的三级缓存过程有着清晰的三个步骤,分别为:

1、createBeanInstance():创建Bean实例,并保存到 singletonFactories 缓存中。

2、populateBean():填充 Bean 的属性,如果遇到循环依赖则取 earlySingletonObjects 缓存中的对象实例,并将该对象实例从 earlySingletonObjects 缓存中移除并加入到 singletonObjects 缓存中,此时Bean对象处于早期创建状态,还未完全初始化。

3、initializeBean():初始化 Bean,并将早期创建状态的 Bean 对象从 earlySingletonObjects 缓存中移除并加入到 singletonObjects 缓存中,此时 Bean 对象处于单例状态。

结束语

以上就是对 SpringBoot 对于 Bean 的三级缓存实现的大概讲解,整个过程实现的源码很多,文章篇幅有限,不能完全解释完整,如果喜欢深究到底的朋友,还是需要自己看源码学习。

对于我的这篇文章讲解,不知道你有没有什么意见呢?请在评论区里留言。[谢谢][谢谢][谢谢]