Spring5——三级缓存和循环依赖

一、Spring 三级缓存

1、三级缓存的定义

看源码的DefaultSingletonBeanRegistry中有三个Map对象,通常情况下,称singletonObjects为一级缓存,earlySingletonObjects为二级缓存,singletonFactories 为三级缓存。

等级 名称 说明
一级 singletonObjects 可以理解为单例池
二级 earlySingletonObjects 早期单例对象缓存
三级 singletonFactories 单例工厂缓存
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

    /** Cache of singleton objects: bean name to bean instance. */
    //一级缓存:单例对象缓存池,beanName->Bean,其中存储的就是实例化,属性赋值成功之后的单例对象
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    /** Cache of singleton factories: bean name to ObjectFactory. */
    //三级缓存:单例工厂的缓存,beanName->ObjectFactory,添加进去的时候实例还未具备属性
    // 用于保存beanName和创建bean的工厂之间的关系map,单例Bean在创建之初过早的暴露出去的Factory,
    // 为什么采用工厂方式,是因为有些Bean是需要被代理的,总不能把代理前的暴露出去那就毫无意义了
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    /** Cache of early singleton objects: bean name to bean instance. */
    //二级缓存:早期的单例对象,beanName->Bean,其中存储的是实例化之后,属性未赋值的单例对象
    // 执行了工厂方法生产出来的Bean,bean被放进去之后,
    // 那么当bean在创建过程中,就可以通过getBean方法获取到
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);、
    
    /** Names of beans that are currently in creation. */
    //三级缓存是用来解决循环依赖,而这个缓存就是用来检测是否存在循环依赖的
    private final Set<String> singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap<>(16)); 
    
}

2、三级缓存的作用

名称 作用
singletonObjects 存放初始化后的单例对象,也就是完成的bean对象
earlySingletonObjects 存放实例化,未完成初始化的单例对象(未完成属性注入的对象),也是用来解决性能问题
singletonFactories 存放ObjectFactory对象,存放的是工厂对象,主要用来解决循环依赖和aop的问题

3、为什么需要第三级缓存?

实际上使用二级缓存就能够实现循环依赖的解决,将二级缓存用来存放早期单例对象(半成品),然后一样的走生命周期的流程,也没有问题,为什么还需要三级缓存?

这个问题反映了spring设计的相当的精妙,如果说,注入的对象实现了aop功能,那么通过循环依赖注入其他bean的时候,bean是原始对象,也就是说没有经过aop的处理,不是最终的代理对象,需要通过三级缓存的ObjectFactory对象工厂才能解决aop的问题,才能获得最终需要的代理对象。

二、循环依赖是如何解决的

假设情景A依赖于B,B依赖于A

首先A通过getBean调到doCreateBean(Spring通过getBean方法获取bean),初步实例化即调用createBeanInstance()方法后,属性注入之前进入到下边代码中

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {
        
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {

        //省略前面的部分代码

        
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
        // 7.判断是否需要提早曝光实例:单例 && 允许循环依赖 && 当前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");
            }
            // 8.提前曝光beanName的ObjectFactory,用于解决循环引用
            // 8.1 应用后置处理器SmartInstantiationAwareBeanPostProcessor,允许返回指定bean的早期引用,若没有则直接返回bean
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

        // Initialize the bean instance.
        // 初始化bean实例。
        Object exposedObject = bean;
        try {
            // 9.对bean进行属性填充;其中,可能存在依赖于其他bean的属性,则会递归初始化依赖的bean实例
            populateBean(beanName, mbd, instanceWrapper);
            // 10.对bean进行初始化
            //初始化bean,过程如下:
            //1:判断是否实现了BeanNameAware,BeanClassLoaderAware,
            //   BeanFactoryAware方法,如果有,则设置相关的属性
            //2: 调用bean初始化的前置(BeanPostProcessor)操作
            //3: 执行初始化的方法。
            //  如果有initializingBean,则调用afterPropertiesSet
            //  如果有InitMethod,则调用初始方法
            //4: 调用bean初始化的后置(BeanPostProcessor)操作
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        
        
        
        //省略中间的部分代码
        
        
        
        // 13.完成创建并返回
        return exposedObject;
    }
}

属性注入之前Spring将Bean包装成一个工厂添加进了三级缓存中,即调用addSingletonFactory()方法

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

    // 这里传入的参数也是一个lambda表达式,() -> getEarlyBeanReference(beanName, mbd, bean)
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {
            // 1.如果beanName不存在于singletonObjects缓存中
            if (!this.singletonObjects.containsKey(beanName)) {
                //往三级缓存里添加
                // 2.将beanName和singletonFactory注册到singletonFactories缓存(beanName -> 该beanName的单例工厂)
                this.singletonFactories.put(beanName, singletonFactory);
                //清除此Bean在二级缓存里的缓存信息
                // 3.移除earlySingletonObjects缓存中的beanName(beanName -> beanName的早期单例对象)
                this.earlySingletonObjects.remove(beanName);
                //这里为了记录注册单例的顺序
                // 4.将beanName注册到registeredSingletons缓存(已经注册的单例集合)
                this.registeredSingletons.add(beanName);
            }
        }
    }
}

这里只是添加了一个工厂(通过这个工厂(ObjectFactory)的getObject方法可以得到一个对象),执行工厂的getObject相当于执行getEarlyBeanReference。那么,什么时候会去调用这个工厂的getObject方法呢?

当A开始属性注入,B开始实例化时,同样会通过getBean,同样会跟A一样属性注入,再次实例化A,此时再次实例化A时,getBean经过getSingleton会从三级缓存中拿出ObjectFactory,调用getObject会拿到A的对象。

到此为止,循环依赖被解决。

解决循环依赖后,会在doCreateBean()方法中调用populateBean()方法填充属性,完成对bean的依赖属性进行注入(@Autowired)。

即在AbstractBeanFactory类中:

  • 通过 getBean(String name)方法、调用doGetBean()方法、在doGetBean()方法内部调用Object sharedInstance = getSingleton(beanName);代码,在getSingleton()方法中会从三级缓存中拿出ObjectFactory

  • 通过ObjectFactory单例对象工厂创建的单例对象,放到早期单例对象缓存中即二级缓存earlySingletonObjects,并在三级缓存中移除beanName对应的单例对象工厂

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

    //一级缓存:单例对象缓存池,beanName->Bean,其中存储的就是实例化,属性赋值成功之后的单例对象
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    //三级缓存:单例工厂的缓存,beanName->ObjectFactory,添加进去的时候实例还未具备属性
    // 用于保存beanName和创建bean的工厂之间的关系map,单例Bean在创建之初过早的暴露出去的Factory,
    // 为什么采用工厂方式,是因为有些Bean是需要被代理的,总不能把代理前的暴露出去那就毫无意义了
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    //二级缓存:早期的单例对象,beanName->Bean,其中存储的是实例化之后,属性未赋值的单例对象
    // 执行了工厂方法生产出来的Bean,bean被放进去之后,
    // 那么当bean在创建过程中,就可以通过getBean方法获取到
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    //三级缓存是用来解决循环依赖,而这个缓存就是用来检测是否存在循环依赖的
    private final Set<String> singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap<>(16));

    @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 1.从单例对象缓存中获取beanName对应的单例对象
        Object singletonObject = this.singletonObjects.get(beanName);
        // 2.如果单例对象缓存中没有,并且该beanName对应的单例bean正在创建中
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            //尝试给一级缓存对象加锁,因为接下来就要对缓存对象操作了
            synchronized (this.singletonObjects) {
                // 4.从早期单例对象缓存中获取单例对象(之所称成为早期单例对象,是因为earlySingletonObjects里
                // 的对象的都是通过提前曝光的ObjectFactory创建出来的,还未进行属性填充等操作)
                //尝试从二级缓存earlySingletonObjects这个存储还没进行属性添加操作的Bean实例缓存中获取
                singletonObject = this.earlySingletonObjects.get(beanName);
                // 5.如果在早期单例对象缓存中也没有,并且允许创建早期单例对象引用
                //如果还没有获取到并且第二个参数为true,为true则表示bean允许被循环引用
                if (singletonObject == null && allowEarlyReference) {
                    // 6.从单例工厂缓存中获取beanName的单例工厂
                    //从三级缓存singletonFactories这个ObjectFactory实例的缓存里尝试获取创建此Bean的单例工厂实例
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    //如果获取到工厂实例
                    if (singletonFactory != null) {
                        // 7.如果存在单例对象工厂,则通过工厂创建一个单例对象
                        singletonObject = singletonFactory.getObject();
                        // 8.将通过单例对象工厂创建的单例对象,放到早期单例对象缓存中
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        // 9.移除该beanName对应的单例对象工厂,因为该单例工厂已经创建了一个实例对象,并且放到earlySingletonObjects缓存了,
                        // 因此,后续获取beanName的单例对象,可以通过earlySingletonObjects缓存拿到,不需要在用到该单例工厂
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        // 10.返回单例对象
        return singletonObject;
    }
}

循环依赖确实被解决了,但是从过程看,似乎ObjectFactory缓存没什么存在的必要?似乎singletonObjects和earlySingletonObjects已经可以解决依赖问题了?
那么三级缓存到底有什么作用呢?我们再仔细分析下singletonFactories

在doCreateBean()方法调用addSingletonFactory()方法,提前曝光beanName的ObjectFactory,用于解决循环引用的时候,会调用getEarlyBeanReference()方法,即addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));方法的时候

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {
        
    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        // 1.如果bean不为空 && mbd不是合成 && 存在InstantiationAwareBeanPostProcessors
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                // 2.应用所有SmartInstantiationAwareBeanPostProcessor,调用getEarlyBeanReference方法
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    // 3.允许SmartInstantiationAwareBeanPostProcessor返回指定bean的早期引用
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }
        // 4.返回要作为bean引用公开的对象,如果没有SmartInstantiationAwareBeanPostProcessor修改,则返回的是入参的bean对象本身
        return exposedObject;
    }
}

它实际上就是调用了后置处理器的getEarlyBeanReference,而真正实现了这个方法的后置处理器只有一个,就是通过@EnableAspectJAutoProxy注解导入的AnnotationAwareAspectJAutoProxyCreator。也就是说如果在不考虑AOP的情况下,上面的代码等价于:

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {
        
    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
    
        return exposedObject;
    }
}

也就是说这个工厂啥都没干,直接将实例化阶段创建的对象返回了!所以说在不考虑AOP的情况下三级缓存有用嘛?讲道理,真的没什么用。

结合了AOP的循环依赖

上面已经说过了,在普通的循环依赖的情况下,使用二级缓存也能解决循环依赖,那么三级缓存就显得可有可无了。其实、三级缓存实际上跟Spring中的AOP相关,我们再来看一看getEarlyBeanReference的代码:

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {
        
    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        // 1.如果bean不为空 && mbd不是合成 && 存在InstantiationAwareBeanPostProcessors
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                // 2.应用所有SmartInstantiationAwareBeanPostProcessor,调用getEarlyBeanReference方法
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    // 3.允许SmartInstantiationAwareBeanPostProcessor返回指定bean的早期引用
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }
        // 4.返回要作为bean引用公开的对象,如果没有SmartInstantiationAwareBeanPostProcessor修改,则返回的是入参的bean对象本身
        return exposedObject;
    }
}

如果在开启AOP的情况下,那么就是调用到AnnotationAwareAspectJAutoProxyCreator的父类的AbstractAutoProxyCreator的getEarlyBeanReference方法,对应的源码如下:

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
        
    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        this.earlyProxyReferences.put(cacheKey, bean);
        // 如果需要代理,返回一个代理对象,不需要代理,直接返回当前传入的这个bean对象
        return wrapIfNecessary(bean, beanName, cacheKey);
    }
}

wrapIfNecessary为Spring实现Bean代理的核心方法

  • wrapIfNecessary在两处会被调用,一处是getEarlyBeanReference,另一处是postProcessAfterInitialization
  • 在wrapIfNecessary方法内部调用getAdvicesAndAdvisorsForBean()返回匹配当前Bean的所有Advice\Advisor\Interceptor,用于判断此该类是否需要创建代理。
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
        
    /**
     * Spring实现Bean代理的核心方法。wrapIfNecessary在两处会被调用,一处是getEarlyBeanReference,
     * 另一处是postProcessAfterInitialization
     */
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        //已经被处理过
        // 1.判断当前bean是否在targetSourcedBeans缓存中存在(已经处理过),如果存在,则直接返回当前bean
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        //不需要被织入逻辑的
        // 2.在advisedBeans缓存中存在,并且value为false,则代表无需处理
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        //是不是基础的bean 是不是需要跳过的
        // 3.bean的类是aop基础设施类 || bean应该跳过,则标记为无需处理,并返回
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        // 返回匹配当前Bean的所有Advice\Advisor\Interceptor
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        // 5.如果存在增强器则创建代理
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            //创建Bean对应的代理,SingletonTargetSource用于封装实现类的信息
            // 5.1 创建代理对象:这边SingletonTargetSource的target属性存放的就是我们原来的bean实例(也就是被代理对象),
            // 用于最后增加逻辑执行完毕后,通过反射执行我们真正的方法时使用(method.invoke(bean, args))
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            // 5.2 创建完代理后,将cacheKey -> 代理类的class放到缓存
            this.proxyTypes.put(cacheKey, proxy.getClass());
            // 返回代理对象
            return proxy;
        }
        //该Bean是不需要进行代理的,下次就不需要重复生成了
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
}

回到上面的例子,我们对A进行了AOP代理的话,那么此时getEarlyBeanReference将返回一个代理后的对象,而不是实例化阶段创建的对象,这样就意味着B中注入的A将是一个代理对象而不是A的实例化阶段创建后的对象。
三级缓存的意义何在?
就以我们上的A、B为例,其中A被AOP代理,我们先分析下使用了三级缓存的情况下,A、B的创建流程

假设不使用三级缓存,直接用二级缓存中,也是可以实现的,那么三级缓存搭配二级缓存的存在有什么意义呢?

上面两个流程的唯一区别在于为A对象创建代理的时机不同,在使用了三级缓存的情况下为A创建代理的时机是在B中需要注入A的时候,而不使用三级缓存的话在A实例化后就需要马上为A创建代理然后放入到二级缓存中去。对于整个A、B的创建过程而言,消耗的时间是一样的(所以常见的三级缓存提高了效率这种说法都是错误的)

上述这种情况下,差别就是在哪里创建代理。如果不用三级缓存,使用二级缓存,违背了Spring在结合AOP跟Bean的生命周期的设计!Spring结合AOP跟Bean的生命周期本身就是通过AbstractAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。

Spring Bean的生命周期:https://www.jianshu.com/p/620bc279e7a2

参考:
https://blog.csdn.net/qq_38762237/article/details/108468333

https://juejin.cn/post/6930904292958142478#heading-5

https://www.cnblogs.com/juniorMa/p/14087517.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容