谈谈我本人了解的Spring为什么肯定要用三级缓存

问题和我了解的论断

二级缓存能解决循环依赖吗?为什么肯定要三级缓存?
能,为了在尽可能放弃原有的bean创立流程不扭转(Spring的bean创立标准)的前提下解决代理循环依赖

集体了解的三级缓存由来

咱们不顺着spring的代码说。先来看看bean的初始化流程有一个整体标准步骤,大抵能够这么形容(其实还存在初始化前置、属性赋值前置等等步骤,但这里不须要关怀所以不列出):
1、实例化
2、属性赋值
3、初始化
4、初始化实现后的后置操作,而AOP的代理包装对象就在这一步实现

遵循这一大前提步骤,如果咱们本人来做这个bean创立流程,首先必定要有一个容器,所以至多一层缓存是须要的,那咱们就先用一层缓存试试,如果只有一个缓存容器,容器的实质就是一个缓存,咱们简略称这个缓存为成品缓存,用来寄存最终成品bean实例

场景一、A依赖B

1、A实例化
2、A属性赋值,发现须要B
3、B实例化,属性赋值,B初始化,B放入成品缓存
4、A拿到成品B,间接赋值
5、A初始化
6、A放入成品缓存
如上,简略的创立流程1个缓存能够解决

场景二、A依赖B,B依赖A

1、A实例化
2、A属性赋值,发现须要B
3、B实例化
4、B属性赋值,发现须要A,但从成品缓存没拿到A,因为这时候A还没创立实现不能放入成品缓存,导致B实例创立失败
如上,只有一个缓存,遇到循环依赖问题就没辙了

那怎么办?

这里有一个核心思想解决思路就是,提前裸露,搞两个缓存,
缓存变为:

  • 一个存最终成品(成品缓存)
  • 一个存半成品(半成品缓存)
    这时候流程如下:
    1、A实例化,并将实例化的A间接放入半成品缓存
    2、A属性赋值,发现须要B
    3、B实例化,并将实例化的B间接放入半成品缓存
    4、B属性赋值,发现须要A,间接从成品缓存拿没拿到,再从半成品缓存拿,拿到了长期A
    5、B初始化
    6、B初始化完,执行后置操作,这里能够把成品B放入成品缓存,并将半成品缓存的B删除
    7、A拿到残缺B,间接赋值实现
    8、A初始化
    9、A初始化完,执行后置操作,成品A放入成品缓存,并将半成品缓存的A删除
    如上,简略的循环依赖问题2个缓存能够解决

场景三、A依赖B,B依赖A,A须要AOP代理

1、A实例化,并将实例化的A间接放入半成品缓存
2、A属性赋值,发现须要B
3、B实例化,并将实例化的B间接放入半成品缓存
4、B属性赋值,发现须要A,间接从成品缓存拿没拿到,再从半成品缓存拿,拿到了长期A
5、B初始化
6、B初始化完,执行后置操作,这里能够把成品B放入成品缓存,并将半成品缓存的B删除
7、A拿到残缺B,间接赋值实现
8、A初始化
9、A初始化完,执行后置操作,因为没有已存在代理包装A1,将A包装成代理A1,并将成品A1放入成品缓存,删除半成品缓存的A
如上,如果用简略的在半成品缓存保留长期对象有一个很显著的问题,就是创立B的时候拿到的是A本体,但A须要被AOP代理,所以其实真正被外界援用的应该是最初的成品A1,这两者不是同一个对象。

那要怎么办?

这里有另一个外围解决思路就是,不间接裸露对象自身而是裸露获取这个对象的工厂,这样每次要用到的时候间接取取工厂,而后工厂判断裸露的对象是否须要代理包装,需要的话就返回代理包装,不须要则返回一般的对象
缓存变为:

  • 一个存最终成品(成品缓存)
  • 一个存获取半成品的工厂(工厂缓存)
    这时候流程如下:
    1、A实例化,并将能够获取实例的工厂A放入工厂缓存
    2、A属性赋值,发现须要B
    3、B实例化,并将能够获取实例的工厂B放入工厂缓存
    4、B属性赋值,发现须要A,间接从成品缓存拿没拿到,再从工厂缓存拿,拿到了工厂A,工厂A返回时发现A须要被代理,所以返回代理A1
    5、B初始化
    6、B初始化完,执行后置操作,把成品B放入成品缓存,并将工厂缓存的B删除
    7、A拿到残缺B,间接赋值实现
    8、A初始化
    9、A初始化完,执行后置操作,因为任何一个缓存里都没有已存在的代理包装A1,将A包装成代理A2,并将成品A1放入成品缓存,删除工厂缓存的工厂A
    如上,很显著,工厂尽管解决了B援用不到A代理对象的问题,然而仍然无奈解决A1和A2不是同一个对象的问题

那要怎么办?

这里就须要引入再次最初一个缓存,那就是用来存工厂创立进去的长期对象的缓存
缓存变为:

  • 一个存最终成品(成品缓存)
  • 一个存工厂创立进去的长期变量(半成品缓存)
  • 一个存获取半成品的工厂(工厂缓存)
    这时候流程如下:
    1、A实例化,并将能够获取实例的工厂A放入工厂缓存
    2、A属性赋值,发现须要B
    3、B实例化,并将能够获取实例的工厂B放入工厂缓存
    4、B属性赋值,发现须要A,从工厂缓存拿,工厂A返回时发现A须要被代理,则生成代理A1,并将A1放入半成品缓存
    5、B初始化
    6、B初始化完,执行后置操作,把成品B放入成品缓存,删除工厂缓存
    7、A拿到残缺B,间接赋值实现
    8、A初始化
    9、A初始化完,执行后置操作,发现半成品缓存已存在代理包装A1,所以不再从新包装代理,间接将A1放入成品缓存,并删除半成品缓存
    至此,各种场景都失去了解决,但我一开始也说了,这一路下来的解决思路都是基于Spring创立bean的大流程前提顺着来的,如果不顺着这个前提流程,可不可以间接用2个缓存解决存在AOP的循环依赖问题呢?
    能。

二级怎么解决aop场景下的循环依赖问题?

还是基于场景三
1、A实例化,而后间接生成代理对象A1,放入半成品缓存(这样即便前面有C也须要用A,拿到的永远是一个对象,没有因为工厂会创立多个对象的问题)
2、A属性赋值,发现须要B
3、B实例化,而后间接生成半成品,放入半成品缓存
4、B属性赋值,发现须要A,间接从半成品缓存拿到A1,赋值实现
5、B初始化
6、B初始化完,执行后置操作,将半成品缓存中的B拿到成品缓存,删除半成品缓存的B
7、A失去残缺B,赋值实现
8、A初始化
9、A初始化完,执行后置操作,发现半成品缓存已存在代理包装A1,将半成品缓存中的A1放到成品缓存,删除半成品缓存

至此我集体认为,二级缓存能够解决所有问题,做成二级缓存须要将代理加强这一步放到实例化之后立马做。然而Spring的整体流程标准是先创建对象,再做加强操作,须要代理的毕竟是多数,所以Spring这里不会为了多数状况扭转整体流程,在保留大流程不变的状况下,他做了三级缓存(起因还是参照后面一步步遇到各种状况下的演变)