Spring的三级缓存整理

spring的三级缓存:

image.png

spring解决循环依赖的核心思想在于提前暴露引用。只有单例的bean会通过三级缓存提前暴露来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以非单例的bean是没有缓存的,不会将其放到三级缓存中。

一级缓存:单例池Map singletonObjects,存放经过实例化和初始化的对象
二级缓存:earlySingletonObjects二级缓存用于存放已经实例化,但未初始化的Bean.保证一个类多次循环依赖时仅构建一次保证单例,没有经过属性填充,不是完整的对像,保证bean的单例,不会重复创建不完整的对象。
如果某个bean出现了循环依赖,就会提前把这个暂时未经过完整生命周期的bean放入earlySingletonObjects中,这个bean如果要经过AOP,那么就会把代理对象放入earlySingletonObjects中,否则就是把原始对象放入earlySingletonObjects,但是不管怎么样,就算是代理对象,代理对象所代理的原始对象也是没有经过完整生命周期的,所以放入earlySingletonObjects我们就可以统一认为是未经过完整生命周期的bean。
earlySingletonObjects能够保证未经过完整生命周期的bean的单例,在解决循环依赖的时候,如果这个不完整的对象已经被放入earlySingletonObjects了,在一个类多次循环依赖时,后面就可以直接从earlySingletonObjects中取了, 不用重复创建不完整的对象。
三级缓存:三级缓存用于存放该Bean的ObjectFactory,当创建一个Bean会先将该Bean包装为ObjectFactory放入三级缓存,第三级可以解决AOP问题。
打破循环依赖的是第三级缓存,第三级缓存将代理的bean或者普通的bean提前暴露了出去,可以进行依赖注入。
第三级缓存使解决循环依赖更方便。


总流程图

入口:
org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)
有循环依赖,才需要提前AOP
改正:在将bean加入三级缓存前,需要判断是否允许循环依赖、是否单例、对象是否在创建中


image.png

第0步:AbstractBeanFactory.doGetBean

// 标记beanName AService是已经创建过至少一次的~~~ 它会一直存留在缓存里不会被移除(除非抛出了异常)
protected <T> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
     if (!typeCheckOnly) {
                this.markBeanAsCreated(beanName);
     }

第一步:

先创建一个普通对象,如果是单例、允许循环依赖、对象正在创建中,则提前暴露引用,将它包装成ObjectFactory放入三级缓存,以下是加入三级缓存的源码


image.png
/**
 * earlySingletonExposure 是一个重要的变量,这里要说明一下。该变量用于表示是否提前暴露
 * 单例 bean,用于解决循环依赖。earlySingletonExposure 由三个条件综合而成,如下:
 *   条件1:mbd.isSingleton() - 表示 bean 是否是单例类型
 *   条件2:allowCircularReferences - 是否允许循环依赖
 *   条件3:isSingletonCurrentlyInCreation(beanName) - 当前 bean 是否处于创建的状态中
 *
 * earlySingletonExposure = 条件1 && 条件2 && 条件3
 *                        = 单例 && 是否允许循环依赖 && 是否存于创建状态中。
 */
    boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);

    if (earlySingletonExposure) {
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
            }
    /**
     * 添加工厂对象到 singletonFactories 缓存中
     * 获取早期 bean 的引用,如果 bean 中的方法被 AOP 切点所匹配到,此时 AOP 相关逻辑会介入
     * getEarlyBeanReference应用后置处理器SmartInstantiationAwareBeanPostProcessor,允许返回指定bean的早期引用,若没有,则直接返回bean
     */
            this.addSingletonFactory(beanName, () -> {
                return this.getEarlyBeanReference(beanName, mbd, bean);
            });
        }

//将bean包装成ObjectFactory,放入三级缓存,同时将bean从二级缓存删除。
//此时的bean没有属性注入,没有初始化,提前进行了引用暴露
      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);
            }

        }
    }


第二步

通过beanName从三级缓存中取出一个lamda表达式,
执行getEarlyBeanReference,如果要进行AOP,则返回的是代理对象,如果不要进行AOP,则返回普通对象。然后将返回的对象加入二级缓存


image.png


第五步

image.png
image.png


第六步

image.png


Spring三级缓存源码流程图

Spring三级缓存.jpg
循环依赖.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容