【Spring】Spring框架为何要使用三级缓存(循环依赖)

循环依赖

说到三级缓存,我们得先来了解一下什么是循环依赖。
我们知道Spring是支持一个Bean引入另外的Bean对象的,那么就不可避免出现相互依赖的问题,Spring是如何解决这个问题呢?

通俗的讲,循环依赖是指Spring中N个Bean相互依赖从而形成一个闭环的现象;即A依赖B,B依赖A。
主要是:A创建时—>需要B---->B去创建—>需要A,从而产生了循环
在这里插入图片描述

那么如何打破这个循环,加个缓存就可以了
在这里插入图片描述
A 的 Bean 在创建过程中,在进行依赖注入之前,先把 A 的原始 Bean 放入缓存(提早暴露,只要放到缓存了,其他 Bean 需要时就可以从缓存中拿了),放入缓存后,再进行依赖注入,此时 A 的Bean 依赖了 B 的 Bean 。

如果 B 的 Bean 不存在,则需要创建 B 的 Bean,而创建 B 的 Bean 的过程和 A 一样,也是先创建一个 B 的原始对象,然后把 B 的原始对象提早暴露出来放入缓存中,然后在对 B 的原始对象进行依赖注入 A,此时能从缓存中拿到 A 的原始对象(虽然是 A 的原始对象,还不是最终的 Bean),B 的原始对象依赖注入完了之后,B 的生命周期结束,那么 A 的生命周期也能结束。

因为整个过程中,都只有一个 A 原始对象,所以对于 B 而言,就算在属性注入时,注入的是 A 原始对
象,也没有关系,因为A 原始对象在后续的生命周期中在堆中没有发生变化。

三级缓存产生的原因

1、解决循环依赖可能导致的死循环问题
2、循环依赖下的AOP代理问题

三级缓存有哪些

三级缓存是指 Spring IOC 容器中用于解决循环依赖问题的一种机制,它包含三个缓存阶段:

singletonObjects 一级缓存

这是 Spring IOC 容器的一级缓存,用于存储已经完全创建并初始化的 Bean实例。当 Bean 完全创建后,它会被放置在这个缓存中。

earlySingletonObjects 二级缓存

这是 Spring IOC 容器的二级缓存,用于存储提前暴露的、尚未完全初始化的 Bean 实例。即循环依赖中需要用到的对象。当 Bean 正在被创建但尚未完成初始化时,它会被放置在这个缓存中。

singletonFactories 三级缓存

这是 Spring IOC 容器的三级缓存,用于存储 Bean 的工厂对象。在创建Bean 实例时,Spring 首先会在这个缓存中查找工厂对象,如果找到则使用该工厂对象来创建 Bean 实例。

三级缓存的作用

三级缓存的作用为了解决循环依赖时的初始化顺序问题。在初始化 Bean 时,Spring 会首先将 Bean 的实例放入三级缓存中,然后进行属性注入等操作。如果发现循环依赖,Spring 会在二级缓存中查找对应的 Bean 实例,如果找到则直接返回,否则会调用Bean的工厂方法来创建 Bean 实例,并将其放入二级缓存中。当 Bean 实例化完成后,会从二级缓存中移除,并放入一级缓存中。

为什么要三级缓存,二级缓存行不行

解决有代理对象(AOP)的循环依赖需要三级缓存,无代理对象的循环依赖则二级缓存即可。

简单的循环依赖使用二级缓存就可以解决,但是如果类中从在代理,使用一级缓存就无法解决,假设A对象中存在AOP代理,B对象获取到原始的A对象,因为A对象中存在代理,A对象会生成一个代理对象,后续会生成完整的A代理对象,那么B对象中依赖的A对象还是原始对象,这个时候就会出现B中的A对象和最终的A对象的值不一致问题。为了解决此问题,使用三级代理,使得A对象更早的暴漏出来,判断是否存在代理,如果存在就返回代理对象,如果不存在就返回原始对象。

三级的缓存存放的是Bean的工厂

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (SmartInstantiationAwareBeanPostProcessor bp ://若为真,则说明有Bean的aop代理, getBeanPostProcessorCache().smartInstantiationAware) {
                exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
        return exposedObject;
    }

这个工厂的作用就是判断这个对象是否需要代理,如果否则直接返回,如果是则返回代理对象。正常的代理的对象初始化后期调用生成的,是基于后置处理器的,若提早的代理就违背了Bean定义的生命周期。所以spring在一个三级缓存放置一个工厂,如果产生循环依赖 ,那么就会调用这个工厂提早的得到代理的对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值