Spring三级缓存的执行细节

前言

都知道spring解决循环依赖问题是通过三级缓存模式;

之所以把spring初始化缓存分为三级,主要是根据spring源码中获取bean的顺序来分的,spring获取bean的代码如下:

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

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

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

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


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

    // 如果找不到,判断是不是正在创建
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 二级缓存:从earlySingletonObjects中找
            singletonObject = this.earlySingletonObjects.get(beanName);

            if (singletonObject == null && allowEarlyReference) {
                // 三级缓存:从singletonFactories中找
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 找到后把bean放到earlySingletonObjects中,并从singletonFactories中删除
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
  • 一级缓存:Map<String, Object> singletonObjects,存储可以用的bean,即:初始化完成的成品bean;
  • 二级缓存:Map<String, Object> earlySingletonObjects,存储bean的实例化对象,该对象还没有完全初始化好的半成本;
  • 三级缓存:Map<String, ObjectFactory<?>> singletonFactories,存储生成bean的工厂,在创建bean阶段;

三级缓存的特点:

  • 一二级缓存保存的是bean的对象,不管是已初始化好的还是未初始化好的,这两个缓存是互斥的,bean不能既在一级缓存又在二级缓存;
  • 所有的bean在创建过程中,都会用到第三级缓存,因为bean对象就是通过beanFactory创建出来的;
  • 所有的bean最终都会落到第一级缓存;
  • 循环依赖,先初始化谁,谁就会入第二级缓存;

大体步骤如下:

  • 创建A bean
  • A beanFactory放进三级缓存
  • 给A bean注入B bean
  • 发现B bean不存在,创建B bean
  • B beanFactory放进三级缓存
  • 给B bean注入A bean
  • 从三级缓存获取到A bean,把A bean放进二级缓存,同时删除三级缓存中的A beanFactory
  • 把A bean注入B bean
  • B初始化完成,调用addSingleton把B bean放进一级缓存,同时删除二级缓存中的B bean和三级缓存中的B beanFactory(注意:二级缓存中没有B bean,只有A bean)
  • 把B bean注入到A bean
  • A初始化完成,调用addSingleton把A bean放进一级缓存,同时删除二级缓存中的A bean和三级缓存中的A beanFactory(注意:三级缓存中已没有A beanFactory)

如图:循环依赖在初始化过程中,一共有8次缓存操作:

  • 三级缓存2次put,2次remove;
  • 二级缓存1次put,1次remove;(都是A bean,循环依赖先初始化谁,谁就会入二级缓存)
  • 一级缓存2次put;

看下三级缓存的入库,出库代码:

singletonFactories(第三级缓存)

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
    throws BeanCreationException {
    // 省略................

    // 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) {
        if (logger.isDebugEnabled()) {
            logger.debug("Eagerly caching bean '" + beanName +
                    "' to allow for resolving potential circular references");
        }

        // 调用父类方法把beanFactory对象放入第三级缓存
        addSingletonFactory(beanName, new ObjectFactory<Object>() {
            @Override
            public Object getObject() throws BeansException {
                return getEarlyBeanReference(beanName, mbd, bean);
            }
        });
    }

    // 省略................
}

父类方法: org.springframework.beans.factory.support.DefaultSingletonBeanRegistry,方法: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)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

earlySingletonObjects(第二级缓存)

上面说了只有循环依赖的时候才会用到第二级缓存,入二级缓存的时机则是在后一个bean依赖注入前一个bean的时候,查询到前一个bean的beanFactory,则生成bean放入二级缓存;

对应代码:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // A bean尚未初始化完成,所以这里返回null
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {

            // 尚未放入二级缓存,这里也返回null
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {

                // 从三级缓存得到beanFactory
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 创建bean对象,放入二级缓存,并清除三级缓存
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

 二级缓存清除/一级缓存插入逻辑

上面说了一二级缓存是互斥的,那么当bean对象完全初始化好以后,则会调用

org.springframework.beans.factory.support.AbstractBeanFactory

protected <T> T doGetBean(

    // 省略。。。。。。。。

    // Create bean instance.
    if (mbd.isSingleton()) {
        // 注意这个Lambda写法,主体是调用getSingleton方法,而第二个参数就是创建完成的bean对象
        sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
            @Override
            public Object getObject() throws BeansException {
                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);
    }

    // 省略。。。。。。。。
}
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "'beanName' must not be null");
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {

            // 省略。。。。。。。。。

            // 看这里,如果是新创建bean就调用addSingleton
            if (newSingleton) {
                addSingleton(beanName, singletonObject);
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }
}

protected void addSingleton(String beanName, Object singletonObject) {

    // 到这里就清楚了,bean入一级缓存,清楚二级缓存

    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}

总结:

spring解决循环依赖的原理并不复杂,也不高深,非要说有什么原理的话,那就是地址引用,A,B相互依赖,在初始化过程中,大可不用等到对象完全初始化好,而只要实例化出一个光杆对象就可以依赖注入了。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值