西河刘卡车医 发表于 2022-6-25 09:39:54

Spring--循环依赖的原理(四)--为什么用三级缓存,而不是二级

原文网址:Spring--循环依赖的原理(四)--为什么用三级缓存,而不是二级_IT利刃出鞘的博客-CSDN博客
简介

说明
        本文介绍Spring(SpringBoot)为什么使用三级缓存来解决循环依赖(为什么不使用二级)。
问题引出
在上边的分析中我们可以提出两个问题:

[*]二级缓存好像没有用到?那么它什么时候会用到?
[*]为什么第三级缓存要用一个工厂,删除第三级缓存,只用第一二级不可以吗?
 文章系列

[*]Spring--循环依赖的原理(一)--什么是循环依赖_IT利刃出鞘的博客-CSDN博客
[*]Spring--循环依赖的原理(二)--打断点分析_IT利刃出鞘的博客-CSDN博客
[*]Spring--循环依赖的原理(三)--原理概述_IT利刃出鞘的博客-CSDN博客
[*]Spring--循环依赖的原理(四)--为什么用三级缓存,而不是二级_IT利刃出鞘的博客-CSDN博客
可以去掉第二级缓存吗?

简介
不可以去掉第二级缓存。
详解
假如有这种情况:a实例同时依赖于b和c,b和c都依赖于a。
        a实例化时,先提前暴露objectFactorya到三级缓存,调用getBean(b)依赖注入b实例。b实例化之后,提前暴露objectFactoryb到三级缓存,调用getBean(a)依赖注入a实例,由于提前暴露了objectFactorya,此时可以从三级缓存中获取到a实例, b实例完成了依赖注入,升级为一级缓存。a实例化再getBean(c)依赖注入c实例,c实例化之后,提前暴露objectFactoryc到三级缓存,调用getBean(a)依赖注入a实例,由于提前暴露了objectFactorya,此时可以从三级缓存中获取到a实例。注意这里又要从三级缓存中获取a实例,我们知道三级缓存中的实例是通过调用singletonFactory.getObject()方法获取的,返回结果每次都可能不一样。如果不用二级缓存,这里会有问题,两次获取的a实例不一样。
可以去掉第三级缓存吗?

概述

结论
不可以去掉第三级缓存。
原因
Spring 的设计原则是在 IOC 结束之后再AOP( bean 实例化、属性设置、初始化之后再通过进行AOP(生成代理对象))。即:AOP的实现需要与bean的生命周期的创建分离。

[*]为了解决循环依赖但又尽量不打破这个设计原则的情况下,使用了第三级缓存(key:bean名字,value:ObjectFactory)。
[*]前边分析过,它是将一个函数式接口作为ObjectFactory,相当于延迟初始化。AOP中发生循环依赖时,通过调用Object的getObject()方法获取到三级缓存中的对象。

[*]如果去掉第三级缓存,将AOP的代理工作放到第二级缓存,这样的话,bean在创建过程中就先生成代理对象再初始化和其他工作,与Spring的AOP的设计原则相违背。
详解

        上边:“bean何时被加入第3级缓存?”  我们可以得到,第三级缓存里存放的value是:AbstractAutowireCapableBeanFactory#getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean)。
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
} 可以看到实际调用SmartInstantiationAwareBeanPostProcessor接口的getEarlyBeanReference(Object bean, String beanName)方法,此接口有以下实现类:
https://img-blog.csdnimg.cn/20210519134959164.png
InstantiationAwareBeanPostProcessorAdapter
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        return bean;
} 直接返回bean,这里看起来第三级缓存没必要存在。但是,看另一个实现类:
AbstractAutoProxyCreator
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        this.earlyProxyReferences.put(cacheKey, bean);
        return wrapIfNecessary(bean, beanName, cacheKey);
} 这里把对象放入第二级缓存。
Spring(SpringBoot)--AOP的原理_IT利刃出鞘的博客-CSDN博客 曾分析过AOP:将横切逻辑织入目标Bean:AbstractAutoProxyCreator#postProcessAfterInitialization方法,在Bean实例化之后被调用。
来看postProcessAfterInitialization
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
                Object cacheKey = getCacheKey(bean.getClass(), beanName);
                if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                        return wrapIfNecessary(bean, beanName, cacheKey);
                }
        }
        return bean;
} 把key对应的项从二级缓存中移除,如果原来二级缓存中就是这个bean,则返回此bean,否则,返回代理类。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: Spring--循环依赖的原理(四)--为什么用三级缓存,而不是二级