Spring为什么使用三级缓存而不是两级解决循环依赖问题?( 二 )


文章插图
与一级缓存架构设计的区别在于:

  • 新增了二级缓存,用于存放刚实例化的bean;
  • 当bean初始化完成后会放入一级缓存,同时将bean从二级缓存中的删除(不需要一式两份,保留一份最终完整的Bean即可)
从目前看这个设计完美解决了bean的完整性问题,但是在实际生产中问题总是叠着问题,没有完美的架构设计 。我们都知道Java中有代理,而且代理的应用非常广泛,包括在Spring中就有非常多的代理,那问题就来了,我们如何区分代理对象与普通对象?如果循环依赖中存在代理对象的循环依赖会发生什么呢?
代理对象的循环依赖在现实开发过程中,我们往往会产生很多的代理对象,当存在代理对象加入到循环依赖流程会是什么样的场景,我们来推演一下,我们仍然使用二级缓存的设计来做推演 。
如果我们在bean初始化完成之后再创建代理对象,整个流程是这样的:
Spring为什么使用三级缓存而不是两级解决循环依赖问题?

文章插图
从上图可以非常直观的看出,最终在一级缓存中的对象A是一个proxy_A,但是对象B依赖的对象A却是一个普通A!很明显现有的设计不能够满足代理对象的循环依赖问题 。
如何解决这个问题呢?我们还是在上一个设计上做修改:
  • 方案一:在获取到A时立即创建代理,如下图(红色部分)所示:

Spring为什么使用三级缓存而不是两级解决循环依赖问题?

文章插图
这个方案看起来解决了B对象依赖不到A的proxy对象问题,但是又引起了一个致命的问题,在A初始化完成之后还会创建一次代理对象,那么就创建了两次代理对象,他们是完全不一样的,这个代理对象不是单例的了!
  • 方案二:在方案一的基础上,我们是不是可以将创建完的proxy_A对象加入到二级缓存中,直接覆盖掉普通A(代理对象会持有普通对象A的引用,所以可以覆盖):这个方案看上去没有问题,但是从设计角度讲,这不符合设计规范,而且覆盖后的A是个代理对象,在后续的操作中,如果再从二级缓存中获取A,这时候就不知道到底获取到的是普通A还是proxy_A了,这无形增加了判断识别的复杂度 。
  • 方案三:在首次实例化A的时候就直接创建A的代理对象,并放入二级缓存中:这个方案与方案二有相同的问题,这里不在赘述 。
解决代理对象的循环依赖之终极方案解决代理对象的循环依赖之终极方案-三级缓存!
Spring为什么使用三级缓存而不是两级解决循环依赖问题?

文章插图
与二级缓存设计最大的不同点在于:
  • 在获取到A时创建proxy_A,同时将其加入到二级缓存中,并返回给B,这样B就依赖了proxy_A;
  • 在A初始化过程中会创建代理对象,这时候会做一个检查,也就是会去查询二级缓存,看有没有proxy_A的存在,如果有说明proxy_A已经创建,我们会选择二级缓存中的proxy_A存入一级缓存并返回(因为二级缓存中的proxy_A已经被B依赖);
其它流程不在赘述,该设计中最重要的几个地方在Spring中的实现是更加细致的,我在流程途中只是简单概括,下面特殊说明一下几个点 。
Spring Bean初始化会产生代理对象的场景在上述流程中,标记位黄色的部分就是两个代理对象的创建的地方,在Spring中就是这两个后置处理器调用的地方,它们分别是:
  • 在调用getEarlyBeanReference时如果实现了BeanPostProcessor则会创建代理对象;
  • 另一个地方是在执行Bean初始化initializeBean时执行BeanPostProcessor会创建代理对象;
在Spring中的第三级缓存有更加灵活设计在Spring中,第三级缓存不仅仅是存入了实例化的对象,而是存入了一个匿名类ObjectFactory,getEarlyBeanReference函数的实现中会调用BeanPostProcessor执行用户自定义的逻辑 。具体代码如下:
// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isDebugEnabled()) {logger.debug("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}// addSingletonFactory方法是将bean加入三级缓存中// 三级缓存会被ObjectFactory包装// getEarlyBeanReference方法会执行Bean的后置处理器addSingletonFactory(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {return getEarlyBeanReference(beanName, mbd, bean);}});}


推荐阅读