spring三级缓存详情 为何采用三级缓存?一级加三级其实就可以解决循环依赖了 为何要加个二级缓存 以及自定义的乞丐版的spring

spring三级缓存详情  为何采用三级缓存?

 

循环依赖相信大家都知道   比如对象A自动注入B 对象B自动注入A  那么如果没有缓存  spring创建A时 检测到A依赖B  但是容器此时还没有B  就会转而创建B  而容器B又依赖A  但是此时容器还没有A 又创建A去了   从而无限递归  造成错误。

 

对于上述情况   

spring将创建对象分为大概三个步骤

1   实例化  在堆中new一个对象 但此时还没有给对象属性赋值

2   给对象赋值

3   类的增加等操作 也就是容器中调用BeanPostProcessor 的实现类的方法(AOP 其实就是这一步实现的  但这一步不止实现AOP )AOP的处理类

AnnotationAwareAspectJAutoProxyCreator 的注解处理类 类关系处理如下图

最终都是利用了spring BeanPostProcessor后置处理器机制   在这个阶段 我们的组件已经被new 且属性也赋值结束  这个时候会把容器中所有的实现了 BeanPostProcessor的类的方法都给调用

public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

在方法中 得到原始类  我们可以根据原始类 从而代理这个类  返回我们的代理类 这也是实现AOP的最基本原理了

4 初始化 (如果这个类实现了 InitializingBean  则会调用此方法完成类的初始化 )

5  返回最终的类(也即是存放到一级缓存中   这里面还是有门道的)

 

 

好了 闲话不说了 回到正题  spring把创建对象分为了五小步

 

对于循环依赖  如果我们可以在创建对象时 的第一步时就将创建的未完全初始化的对象放在一个缓存中  比如说创建A对象的时候

在new后 就将其放入一个Map 里面 在对A对象赋值的时候  检测到B需要创建 从而去创建B  B检测到需要对象A时 就从这个缓存中去拿  这样就不会无限递归  出现错误   解决循环依赖   一级缓存就足以处理这个问题     但是这样有个大问题?是什么呢?想一想?

对  就是类的增加  比如我们的A类被代理了  那我们开始存的是A类原始类 那么B的依赖也就被赋值为A的原始类了 不是被增强的A类了  所以一级缓存是不能解决这个问题的 所以 spring引入了三级缓存 下面详细叙说思路:

注  spring的三级依赖在源码中 存在于:

 DefaultSingletonBeanRegistry 类中以下三个变量:
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
//一级缓存  存放的是 完整的单例对象 属性完整 且被增强后的对象(若被增强了(包含代理))
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
//二级缓存  存放的是  属性未被赋值  但是已经被代理的对象(若被代理)
private final Map<String, Object> earlySingletonObjects = new HashMap(16);

 //三级缓存  存放的是  一个代码块  这个代码块  的功能就是提前完成Aop (若被代理 如果没有被代理则返回原始对象) 并返回产生的对象

对于代码块  对于熟悉java lumda 函数式编程式的同学来说   就知道这个代码块只会在用到的时候才会执行 不用到的时候是不会执行的  

spring的思路是如下:

 

在创建A对象的时候    在三级缓存存放了能产生提前产生A对象AOP的代理对象的 代码块    这一步  然后返回A的原始对象

然后 A原始对象开始属性赋值  检测到B对象不存在  这一步 spring会从一级缓存依次到三级缓存找  没有找到则说明这个依赖没被创建 所以 转而去创建B对象

对于B对象来说第一步与A的类似   在三级缓存存放了能产生提前产生B对象AOP的代理对象的 代码块   这一步 会返回B的原始对象

然后开始对B属性赋值  B对象有个依赖A   然后就会从从一级缓存依次到三级缓存找  很明显 三级缓存存在有A的能产生A的提前AOP的代码块  就会执行这个代码块 返回一个对象 (这个对象会根据我们自己配置的情况返回原始对象还是被代理的对象)然后将这个属性赋值给对象B  (请注意这一步会把生成的对象放到二级缓存中  为什么后续再说)

执行B的第三步

B的AOP操作是否已经被提前执行了  所以这一步其实会有一个判定条件  判定是否已经提前被代理了 如果没有才会执行AOP的增强操作

执行B的第四步

执行B的第五步

这一步会判断返回的是B的原始类还是B的代理类

B对象创建结束  回到A的创建过程中 也就是第二步 属性赋值

 

执行A的第三步

对于这一步  A的AOP操作已经被提前执行了  所以这一步其实会有一个判定条件  判定是否已经提前被代理了 如果没有才会执行AOP的增强操作 ( 很明显A被提前Aop了所以 A不会执行了这一步的AOP增强操作了)

执行A的第四步

执行A的第五步

这一步会判断返回的是A的原始类还是A的代理类 很明显  A 被提前代理了  返回的应该是代理类

 

 

其实用一级缓存加三级缓存就可以解决循环依赖的问题了  但是为什么要采用二级缓存了 ?试想一下 加入对象A循环依赖B C 也就是A与B A与C都存在循环依赖  在实例化A的时候会提前实例化B和C 在示例化B的时候 已经提前利用三级缓存生成了A的代理类 但是由于A的属性赋值这个阶段还没结束 一级缓存还没有A 所以在实例化C的时候  又要从三级缓存中拿代码块生成A的代理类 这这样 对我们的程序是没问题的 因为代理类依托的原始类一直都是A 所以程序功能是没影响的  但是这样存在性能的浪费  如果我们把代码快生成的A的代理类 存放到二级缓存中  那么C需要的时候就可以直接获取到这个类了 从而不会又执行代码块生成一个了   

 

自己写的乞丐版 spring  按照上述思路 解决循环依赖:gitHub地址

https://github.com/pupansheng/pps-spring.git

效果图:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值