Spring--循环依赖的原理(四)--为什么用三级缓存,而不是二级 ...

打印 上一主题 下一主题

主题 705|帖子 705|积分 2115

原文网址:Spring--循环依赖的原理(四)--为什么用三级缓存,而不是二级_IT利刃出鞘的博客-CSDN博客
简介

说明
        本文介绍Spring(SpringBoot)为什么使用三级缓存来解决循环依赖(为什么不使用二级)。
问题引出
在上边的分析中我们可以提出两个问题:

  • 二级缓存好像没有用到?那么它什么时候会用到?
  • 为什么第三级缓存要用一个工厂,删除第三级缓存,只用第一二级不可以吗?
 文章系列
可以去掉第二级缓存吗?

简介
不可以去掉第二级缓存。
详解
假如有这种情况:a实例同时依赖于b和c,b和c都依赖于a。
        a实例化时,先提前暴露objectFactorya到三级缓存,调用getBean(b)依赖注入b实例。b实例化之后,提前暴露objectFactoryb到三级缓存,调用getBean(a)依赖注入a实例,由于提前暴露了objectFactorya,此时可以从三级缓存中获取到a实例, b实例完成了依赖注入,升级为一级缓存。a实例化再getBean(c)依赖注入c实例,c实例化之后,提前暴露objectFactoryc到三级缓存,调用getBean(a)依赖注入a实例,由于提前暴露了objectFactorya,此时可以从三级缓存中获取到a实例。注意这里又要从三级缓存中获取a实例,我们知道三级缓存中的实例是通过调用singletonFactory.getObject()方法获取的,返回结果每次都可能不一样。如果不用二级缓存,这里会有问题,两次获取的a实例不一样。
可以去掉第三级缓存吗?

概述

结论
不可以去掉第三级缓存。
原因
Spring 的设计原则是在 IOC 结束之后再AOP( bean 实例化、属性设置、初始化之后再通过进行AOP(生成代理对象))。即:AOP的实现需要与bean的生命周期的创建分离。

  • 为了解决循环依赖但又尽量不打破这个设计原则的情况下,使用了第三级缓存(key:bean名字,value:ObjectFactory)。

    • 前边分析过,它是将一个函数式接口作为ObjectFactory,相当于延迟初始化。AOP中发生循环依赖时,通过调用Object的getObject()方法获取到三级缓存中的对象。

  • 如果去掉第三级缓存,将AOP的代理工作放到第二级缓存,这样的话,bean在创建过程中就先生成代理对象再初始化和其他工作,与Spring的AOP的设计原则相违背。
详解

        上边:“bean何时被加入第3级缓存?”  我们可以得到,第三级缓存里存放的value是:AbstractAutowireCapableBeanFactory#getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean)。
  1. protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
  2.     Object exposedObject = bean;
  3.     if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
  4.         for (BeanPostProcessor bp : getBeanPostProcessors()) {
  5.             if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
  6.                 SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
  7.                 exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
  8.             }
  9.         }
  10.     }
  11.     return exposedObject;
  12. }
复制代码
可以看到实际调用SmartInstantiationAwareBeanPostProcessor接口的getEarlyBeanReference(Object bean, String beanName)方法,此接口有以下实现类:

InstantiationAwareBeanPostProcessorAdapter
  1. @Override
  2. public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
  3.         return bean;
  4. }
复制代码
直接返回bean,这里看起来第三级缓存没必要存在。但是,看另一个实现类:
AbstractAutoProxyCreator
  1. @Override
  2. public Object getEarlyBeanReference(Object bean, String beanName) {
  3.         Object cacheKey = getCacheKey(bean.getClass(), beanName);
  4.         this.earlyProxyReferences.put(cacheKey, bean);
  5.         return wrapIfNecessary(bean, beanName, cacheKey);
  6. }
复制代码
这里把对象放入第二级缓存。
Spring(SpringBoot)--AOP的原理_IT利刃出鞘的博客-CSDN博客 曾分析过AOP:将横切逻辑织入目标Bean:AbstractAutoProxyCreator#postProcessAfterInitialization方法,在Bean实例化之后被调用。
来看postProcessAfterInitialization
  1. @Override
  2. public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
  3.         if (bean != null) {
  4.                 Object cacheKey = getCacheKey(bean.getClass(), beanName);
  5.                 if (this.earlyProxyReferences.remove(cacheKey) != bean) {
  6.                         return wrapIfNecessary(bean, beanName, cacheKey);
  7.                 }
  8.         }
  9.         return bean;
  10. }
复制代码
把key对应的项从二级缓存中移除,如果原来二级缓存中就是这个bean,则返回此bean,否则,返回代理类。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

西河刘卡车医

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表