VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > temp > 简明python教程 >
  • 从源码角度,带你研究什么是三级缓存

  • Java开发,总会遇到问三级缓存的。
  • 看了很多文章,感觉不是很透彻。打算自己写一个自以为很详细的对三级缓存的理解。 有图文。也有文字概括。受不了动图的可以看文字概括哦

进入正题:

  • 在开发过程中会遇到循环依赖的问题。 就跟下图一样

image

  • Spring在为此设计了三级缓存来解决以上依赖的问题

首先我们得知道 三级缓存里面分别存的什么

一级缓存里存的是成品对象,实例化和初始化都完成了,我们的应用中使用的对象就是一级缓存中的

二级缓存中存的是半成品,用来解决对象创建过程中的循环依赖问题

三级缓存中存的是 ObjectFactory<?> 类型的 lambda 表达式,用于处理存在 AOP 时的循环依赖问题

image

Spring 三级缓存的顺序

三级缓存的顺序是由查询循序而来,与在类中的定义顺序无关

image

所以第一级缓存:singletonObjects ,第二级缓存:earlySingletonObjects ,第三级缓存:singletonFactories

Spring 的的注入方式有三种:构造方法注入、setter 方法注入、接口注入

接口注入的方式太灵活,易用性比较差,所以并未广泛应用起来,大家知道有这么一说就好,不要去细扣了

构造方法注入的方式,将实例化与初始化并在一起完成,能够快速创建一个可直接使用的对象,但它没法处理循环依赖的问题,了解就好

setter 方法注入的方式,是在对象实例化完成之后,再通过反射调用对象的 setter 方法完成属性的赋值,能够处理循环依赖的问题,是后文的基石,必须要熟悉

Spring 源码分析

  • 下面会从几种不同的情况来进行源码跟踪

1、 没有依赖,有 AOP

代码非常简单: spring-no-dependence

image

  • 上图的逻辑就是 ClassPathXmlApplicationContext#refresh

  • -> this.finishBeanFactoryInitialization(beanFactory)

  • -> beanFactory.preInstantiateSingletons()

  • -> isFactoryBean 判断bean, 然后调用 getBean 方法。

  • 接下来调用 doGetBean 方法

image

  • 上图逻辑是 DefaultSingletonBeanRegistry#getSingleton 判断是否存在缓存当中,如果没有则进行创建Bean
  • 可以观察到
 Map<String, Object> singletonObjects               无 simpleBean 
 Map<String, Object> earlySingletonObjects          无 simpleBean 
 Map<String, ObjectFactory<?>> singletonFactories   无 simpleBean 
 Set<String> singletonsCurrentlyInCreation          有 simpleBean 
  • 说明bean在创建过成中
  • 我们接着从 createBean 往下跟

image

  • 关键代码在 doCreateBean 中,其中有几个关键方法的调用值得大家去跟下
 Map<String, Object> singletonObjects               存储的 simpleBean 的代理对象
 Map<String, Object> earlySingletonObjects          由始至终都没有 simpleBean 对象
 Map<String, ObjectFactory<?>> singletonFactories   存入了一会数据立马就删除了 并未使用过  
 
 // 以下说的方法均在 AbstractAutowireCapableBeanFactory 类下
 // createBeanInstance 通过反射完成对象的实例化,获得半成品对象。 给分配内存空间, 即使半成品
 // populateBean 填充半成品属性, 如果有依赖对象则在这里引入
 // initializeBean 初始化半成品对象
 // applyBeanPostProcessorsAfterInitialization BeanPostProcessor的后置处理,AOP 的代理对象替换就是在这里完成的  
  • 此时:代理对象的创建是在对象实例化完成,并且初始化也完成之后进行的,是对一个成品对象创建代理对象
  • 所以《没有依赖,有 AOP》 情况下:只用一级缓存就够了,其他两个缓存可以不要也能完成对象

2、循环依赖,没有AOP

代码依旧非常简单: spring-circle-simple 此时循环依赖的两个类是: Circle 和 Loop

对象的创建过程与前面的基本一致,只是多了循环依赖,少了 AOP,所以我们重点关注: populateBean 和 initializeBean 方法

先创建的是 Circle 对象,那么我们就从创建它的 populateBean 开始,再开始之前,我们先看看三级缓存中的数据情况

 Map<String, Object> singletonObjects               无 circle 也无 loop 对象
 Map<String, Object> earlySingletonObjects          无 circle 也无 loop 对象
 Map<String, ObjectFactory<?>> singletonFactories   只有 cicle 的 lambda
 Set<String> singletonsCurrentlyInCreation          只有 circle 

image

  • 我们开始跟populateBean,它完成属性的填充,与循环依赖有关,一定要仔细看,仔细跟

image

对 circle 对象的属性 loop 进行填充的时候,去 Spring 容器中找 loop 对象,发现没有则进行创建,又来到了熟悉的 createBean

此时三级缓存中的数据没有变化,但是 Set<String> singletonsCurrentlyInCreation 中多了个 loop 标识loop正在创建中

image

loop 实例化完成之后,对其属性 circle 进行填充,去 Spring 中获取 circle 对象,又来到了熟悉的 doGetBean

此时一、二级缓存 (singletonObjects``earlySingletonObjects) 中都没有 circleloop ,而三级缓存中有这两个

image

通过 getSingleton 获取circle时,三级缓存调用了 getEarlyBeanReference ,但由于没有 AOP,所以getEarlyBeanReference 直接返回了普通的 半成品 circle

然后将 半成品 circle 放到了二级缓存,并将其返回,然后填充到了 loop 对象中

此时的 loop 对象就是一个成品对象了;接着将 loop 对象返回,填充到 circle 对象中,如下如所示

image

我们发现直接将 成品 loop 放到了一级缓存中,二级缓存自始至终都没有过 loop ,三级缓存虽说存了 loop ,但没用到就直接 remove 了

此时缓存中的数据,相信大家都能想到了

 Map<String, Object> singletonObjects               无 circle 有 loop 对象
 Map<String, Object> earlySingletonObjects          有 circle 无 loop 对象
 Map<String, ObjectFactory<?>> singletonFactories   无 circle 也无 loop
 Set<String> singletonsCurrentlyInCreation          有 circle 无 loop 因为loop创建完毕了
  • 当 loop 对象完成 创建bean的时候 会调用 DefaultSingletonBeanRegistry#getSingleton -> DefaultSingletonBeanRegistry#addSingleton
  • 将数据对象移动到一级缓存中。二级缓存的 circle 没用上就删除了, 只有 circle 存在三级缓存的数据被调用到了。将半成品的 circle 给返回给 loop对象
  • 所以《循环依赖,没有AOP》情况下:可以减少某个缓存,只需要两级缓存就够了

概括:(循环依赖,没有AOP)

  • 上头的步骤可概括为:
  • 第一步。doCreateBean 进行 circle 的创建,创建步骤为:
  • circle 的流程:
- `AbstractBeanFactory#doGetBean`                               获取bean
- -> `AbstractAutowireCapableBeanFactory#createBean`            创建bean
- -> `AbstractAutowireCapableBeanFactory#doCreateBean`          开始创建bean
- -> `AbstractAutowireCapableBeanFactory#addSingletonFactory`   把bean的一个 lambda 到三级缓存去了 singletonFactories
- -> `AbstractAutowireCapableBeanFactory#populateBean`          填充bean
- -> `AbstractAutowireCapableBeanFactory#applyPropertyValues`   检查到有要添加的一来 进行填充
- -> `BeanDefinitionValueResolver#resolveValueIfNecessary`      注意 ! 这个位置获取 loop 对象
```java
- 断点 我们观察下 三个缓存 Map的存储情况
```java 
 Map<String, Object> singletonObjects               无 circle 也无 loop 对象
 Map<String, Object> earlySingletonObjects          无 circle 也无 loop 对象
 Map<String, ObjectFactory<?>> singletonFactories   有 circle 也无 loop 对象
 Set<String> singletonsCurrentlyInCreation          有 circle 
  • 第二步 然后 get loop Bean 会重复上面的步骤
- `AbstractBeanFactory#doGetBean`                               获取bean
- -> `AbstractAutowireCapableBeanFactory#createBean`            创建bean
- -> `AbstractAutowireCapableBeanFactory#doCreateBean`          开始创建bean
- -> `AbstractAutowireCapableBeanFactory#addSingletonFactory`   把bean的一个 lambda 到三级缓存去了 singletonFactories
- -> `AbstractAutowireCapableBeanFactory#populateBean`          填充bean
- -> `AbstractAutowireCapableBeanFactory#applyPropertyValues`   检查到有要添加的一来 进行填充
- -> `BeanDefinitionValueResolver#resolveValueIfNecessary`      注意 ! 这个位置改了。获取的是 circle 对象
  • 断点 我们观察下 三个缓存 Map的存储情况
 Map<String, Object> singletonObjects               无 circle 也无 loop 对象
 Map<String, Object> earlySingletonObjects          无 circle 也无 loop 对象
 Map<String, ObjectFactory<?>> singletonFactories   有 circle 有 loop 对象
 Set<String> singletonsCurrentlyInCreation          有 circle 有 loop 说明两个对象都在创建中 
  • 关键点来了:
  • 第三步 相当于程序是第二次进入 circle 的 AbstractBeanFactory#doGetBean
- `AbstractBeanFactory#doGetBean`                                           第二次获取 circle
- `AbstractBeanFactory#getSingleton(beanName)`                              获取 Bean 的缓存
- `DefaultSingletonBeanRegistry#getSingleton(beanName, true)`               获取 Bean 的缓存
- `DefaultSingletonBeanRegistry#isSingletonCurrentlyInCreation(beanName)`   关键!! 判断 circle 这个名字的bean是不是在创建过程
- `this.singletonFactories.get(beanName)`                                   获取这个 circle 的 lambda 创建函数
- `singletonFactory.getObject()`                                            调用函数 获取了一个半成品的对象。 也就是 loop 还为空的 circle对象
- `this.earlySingletonObjects.put(beanName, singletonObject)`               将对象加入到二级缓存里面去   earlySingletonObjects 增加了对象
// 附,只有 earlySingletonObjects 新增了一个 circle 对象,其他map 无改变。 并且loop的 singletonFactories 也未使用到
  • 然后就返回了 circle 给到 loop 进行属性填充
  • 完成 loop 创建 将 loop 在 (earlySingletonObjects、singletonFactories、singletonsCurrentlyInCreation)清除。loop添加对象到 singletonObjects
  • 返回创建好的 loop 给到 circle 的填充属性流程
  • 填充完毕之后。在(earlySingletonObjects、singletonFactories、singletonsCurrentlyInCreation)清除。 添加circle对象到 singletonObjects
  • 注意 : circle 就算只是半成品 那他也是在bean中是唯一的。 只要 circle 的属性在后面填充了loop 那么在 loop 的那个单例缓存里面。就会有循环依赖的 circle 对象
  • 其实在整个流程中 circle 会进入到二级缓存当中。但是没使用。就被remove了
  • loop 在二级缓存从来就没有出现过。因为不会进入两次 loop 的 doGetBean流程 。 loop的三级缓存数据也没使用过就被删除了。

2、循环依赖,有AOP

代码还是非常简单:spring-circle-aop ,在循环依赖的基础上加了 AOP

比上一种情况多了 AOP,我们来看看对象的创建过程有什么不一样;同样是先创建 Circle ,在创建Loop

创建过程与上一种情况大体一样,只是有小部分区别,跟源码的时候我会在这些区别上有所停顿,其他的会跳过,大家要仔细看

实例化 Circle ,然后填充 半成品 circle 的属性 loop ,去 Spring 容器中获取 loop 对象,发现没有

则实例化 Loop ,接着填充 半成品 loop 的属性 circle ,去 Spring 容器中获取 circle 对象 

这个过程与前一种情况是一致的,就直接跳过了,此时三级缓存中的数据如下:

 Map<String, Object> singletonObjects               无 circle 也无 loop 对象
 Map<String, Object> earlySingletonObjects          无 circle 也无 loop 对象
 Map<String, ObjectFactory<?>> singletonFactories   有 circle 有 loop 对象
 Set<String> singletonsCurrentlyInCreation          有 circle 有 loop 说明两个对象都在创建中 

image

我们发现从第三级缓存获取 circle 的时候,调用了 getEarlyBeanReference 创建了 半成品circle的代理对象

将 半成品 circle 的代理对象放到了第二级缓存中,并将代理对象返回赋值给了 半成品 loop 的 circle 属性

注意:此时是在进行 loop 的初始化,但却把 半成品 circle 的代理对象提前创建出来了

loop 的初始化还未完成,我们接着往下看,又是一个重点,仔细看

image

在 initializeBean 方法中完成了 半成品 loop 的初始化,并在最后创建了 loop 成品 的代理对象

loop 代理对象创建完成之后会将其放入到第一级缓存中(移除第三级缓存中的 loop ,第二级缓存自始至终都没有 loop )

然后将 loop 代理对象返回并赋值给 半成品 circle 的属性 loop ,接着进行 半成品 circle 的 initializeBean

image

因为 circle 的代理对象已经生成过了(在第二级缓存中),所以不用再生成代理对象了;将第二级缓存中的 circle 代理对象移到第一级缓存中,并返回该代理对象

此时各级缓存中的数据情况如下(普通circle loop 对象在各自代理对象的 target 中)

 Map<String, Object> singletonObjects               有 circle 代理对象 有 loop 代理对象
 Map<String, Object> earlySingletonObjects          无 circle 无 loop 对象
 Map<String, ObjectFactory<?>> singletonFactories   无 circle 无 loop 对象
 Set<String> singletonsCurrentlyInCreation          无 circle 无 loop 

我们回顾下这种情况下各级缓存的存在感,一级缓存仍是存在感十足,二级缓存有存在感,三级缓存挺有存在感

      第三级缓存提前创建 circle 代理对象,不提前创建则只能给 loop 对象的属性 circle 赋值成 半成品 circle ,那么 loop 对象中的 circle 对象就无 AOP 增强功能了

      第二级缓存用于存放 circle 代理,用于解决循环依赖;也许在这个示例体现的不够明显,因为依赖比较简单,依赖稍复杂一些,就能感受到了
      
第一级缓存存放的是对外暴露的对象,可能是代理对象,也可能是普通对象

所以此种情况下:三级缓存一个都不能少

概括: (2、循环依赖,有AOP)

  • 与 概括:(循环依赖,没有AOP)基本一致
  • 在第三步发生变化:
  •  
- `AbstractBeanFactory#doGetBean`                                           第二次获取 circle
- `AbstractBeanFactory#getSingleton(beanName)`                              获取 Bean 的缓存
- `DefaultSingletonBeanRegistry#getSingleton(beanName, true)`               获取 Bean 的缓存
- `DefaultSingletonBeanRegistry#isSingletonCurrentlyInCreation(beanName)`   判断 circle 这个名字的bean是不是在创建过程
- `this.singletonFactories.get(beanName)`                                   获取这个 circle 的 lambda 创建函数 
- `singletonFactory.getObject()`                                            调用函数 获取了一个半成品的对象。(注意!! 有AOP环绕的对象在该位置会创建代理对象, 并且将代理对象 通过 AbstractAutoProxyCreator#getEarlyBeanReference 同步到AOP的创建类里边。为了后面的使用) 也就是 loop 还为空的 circle对象
- `this.earlySingletonObjects.put(beanName, singletonObject)`               将对象加入到二级缓存里面去   earlySingletonObjects 增加了对象
// 附,只有 earlySingletonObjects 新增了一个 circle 对象,其他map 无改变。 
  • 然后就完成了 loop 的创建。
  • 然后进行完 circle 填充之后。
- -> `AbstractAutowireCapableBeanFactory#populateBean`          填充完bean之后
- -> `AbstractAutowireCapableBeanFactory#initializeBean`        进行 circle 的初始化
- -> `AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization`        bean的后置通知。此位置会进行 bean AOP的环绕 返回代理对象
- 由于在上方 loop 获取 circle 的时候不是已经创建了个代理对象了吗。那么这个aop就不能在新建一个代理类了。不然不一致
- 接着往下看
- -> `AbstractAutoProxyCreator#postProcessAfterInitialization`        创建代理对象
- -> `if (this.earlyProxyReferences.remove(cacheKey) != bean)`        这个时候  二级缓存派上用场了。在这里。判断是否已经有代理类了。如果有代理类则不新建代理类对象。
// 这样 circle 的代理就不会被重复创建了。  二级缓存也派上了用场

4、循环依赖 + AOP + 删除第三级缓存

没有依赖,有AOP 这种情况中,我们知道 AOP 代理对象的生成是在成品对象创建完成之后创建的,这也是 Spring 的设计原则,代理对象尽量推迟创建

循环依赖 + AOP 这种情况中, circle 代理对象的生成提前了,因为必须要保证其 AOP 功能,但 loop 代理对象的生成还是遵循的 Spring 的原则

如果我们打破这个原则,将代理对象的创建逻辑提前,那是不是就可以不用三级缓存了,而只用两级缓存了呢?

代码依旧简单:spring-circle-custom ,只是对 Spring 的源码做了非常小的改动,改动如下

image

去除了第三级缓存,并将代理对象的创建逻辑提前,置于实例化之后,初始化之前;

总结

  1、三级缓存各自的作用

    第一级缓存存的是对外暴露的对象,也就是我们应用需要用到的

    第二级缓存的作用是为了处理循环依赖的对象创建问题,里面存的是半成品对象或半成品对象的代理对象

    第三级缓存的作用处理存在 AOP + 循环依赖的对象创建问题,能将代理对象提前创建

  2、Spring 为什么要引入第三级缓存

    严格来讲,第三级缓存并非缺它不可,因为可以提前创建代理对象

    提前创建代理对象只是会节省那么一丢丢内存空间,并不会带来性能上的提升,但是会破环 Spring 的设计原则

    Spring 的设计原则是尽可能保证普通对象创建完成之后,再生成其 AOP 代理(尽可能延迟代理对象的生成)

    所以 Spring 用了第三级缓存,既维持了设计原则,又处理了循环依赖;牺牲那么一丢丢内存空间是愿意接受的

 

原文:https://www.cnblogs.com/yunlongn/p/15638300.html

相关教程