spring如何使用三级缓存解决循环依赖

1. 前言

在日常开发中,Bean之间的循环依赖非常常见,Spring 已经帮我们做到使用无感知处理,那么 Spring 是如何实现的呢?

2. 循环依赖简介

2.1 什么是循环依赖

循环依赖是指两个或多个对象存在相互依赖、相互引用的关系,而这种引用形成一个环时,就会出现循环引用,如图:

kotlin 复制代码
public class PersonA { 
  @Autowired
  private PersonB personB;
}
kotlin 复制代码
public class PersonB {
  @Autowired
  private PersonA personA;
}

2.2 Spring 处理循环依赖的前提条件

1.相互依赖的 Bean 必须为单利;

因为如果每次请求都创建一个 Bean,那么在处理循环依赖的时候,每次都会产生一个新的 Bean 实例,由于没有全局的实例 Bean 缓存,则无法处理循环依赖

2.依赖注入的方式不能都是构造函数注入的方式。

使用构造函数注入,Bean 实例在构造函数没有完全被调用时是不会创建的;因为 PersonA 引用 PersonB,PersonB 又引用 PersonA,两者都无法进行初始化,产生了死锁

3. 三级缓存原理

3.1 什么是三级缓存

Spring 是通过三级缓存的方式处理循环依赖,三级缓存是 Spring Bean 在各个阶段的缓存

一级缓存(SingletonObjects):

存放已经完全实例化、初始化的bean,实现了单利bean只会实例化和初始化一次

二级缓存(EarlySingletonObjects):

存放早期暴露出来的Bean对象,bean的生命周期还未完成(未完成属性注入与初始化的bean)

三级缓存(SingletonFactories):

三级缓存中存储的是单利工厂缓存,通过调用该对象的 GetObject 方法,可以获取到早期暴露出去的 Bean;在该 Bean 要被其他 Bean 引用时,Spring 就会用工厂对象创建出该 Bean 的实例对象,最终当该 Bean 完成构造的所有步骤后就会将该 Bean 放入到一级缓存中

typescript 复制代码
/** 一级缓存 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** 二级缓存 */ 
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16); 
/** 三级缓存 */ 
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

3.2 三级缓存流程

3.3 三级缓存源码解析

创建 Bean 主要的方法是 AbstractBeanFactory.doGetBean 方法

less 复制代码
protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
  // 获取bean的规范名称
  String beanName = transformedBeanName(name); 
  Object bean;  
  // 从各级缓存中获取bean对象 
  Object sharedInstance = getSingleton(beanName); 
  // 跟factoryBean相关判断
  if (sharedInstance != null && args == null) { 
...
  } 
  // 获取factorybean的真是bean
  //若为普通bean则直接返回对象
​
  bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); 
} 
...... 
  // 创建单利bean对象 
  if (mbd.isSingleton()) { 
    sharedInstance = getSingleton(beanName, () -> { 
  try {
    // 创建bean
    return createBean(beanName, mbd, args); 
  } catch (BeansException ex) { 
    // Explicitly remove instance from singleton cache: It might have been put there 
    // eagerly by the creation process, to allow for circular reference resolution. 
    // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); 
    throw ex; 
  } 
}); 
  bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); 
} 
......    
// 返回bean对象 return (T) bean; }
​

我们看两个比较重要获取 Bean 的方法 GetSingleton()

kotlin 复制代码
// 这个方法主要是三级缓存容器,思路大概是:从一级缓存查询,若找不到去二级缓存查询,还是不存在则去三级缓存,若三级缓存找到了,则将bean放入二级缓存中
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  // 从一级缓存中查找bean
  Object singletonObject = this.singletonObjects.get(beanName);
  // 判断一级缓存查找不到bean && bean是否处于创建中,成立,则进入循环依赖
  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    synchronized (this.singletonObjects) {
    // 从二级缓存中查找
    singletonObject = this.earlySingletonObjects.get(beanName);
    // 二级缓存未查询到 && 是否允许获取早期引用
    if (singletonObject == null && allowEarlyReference) {
      // 从三级缓存查询
      ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
      // 三级缓存存在bean
      if (singletonFactory != null) {
        // 获取bean实例
        singletonObject = singletonFactory.getObject();
        // 从三级缓存升级到二级缓存,
        this.earlySingletonObjects.put(beanName, singletonObject);
        // 三级缓存中移除
        this.singletonFactories.remove(beanName);
        }
      }
    }
  }
   // 返回bean
  return singletonObject;
}
kotlin 复制代码
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {  
  Assert.notNull(beanName, "Bean name must not be null");  
  synchronized (this.singletonObjects) {        
  // 从一级缓存中获取对应bean 
  Object singletonObject = this.singletonObjects.get(beanName);      
  // 若bean不存在 
  if (singletonObject == null) {  
    // 当前正在销毁bean,不能创建
    if (this.singletonsCurrentlyInDestruction) {  
      throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)");  
    }  
    if (logger.isDebugEnabled()) {  
      logger.debug("Creating shared instance of singleton bean '" + beanName + "'");  
    }
    // 创建前检查,记录正在加载状态     
    beforeSingletonCreation(beanName); 
    boolean newSingleton = false;
    boolean recordSuppressedExceptions = (this.suppressedExceptions == null);  
    // 如果当前没有异常,初始化异常集合
    if (recordSuppressedExceptions) {  
      this.suppressedExceptions = new LinkedHashSet<>();  
    }  
    try {
      // 执行匿名内部类方法 
      singletonObject = singletonFactory.getObject(); 
      newSingleton = true; 
    }  
    catch(IllegalStateException ex){  
      // 执行getObject方法创建bean
      singletonObject = this.singletonObjects.get(beanName);  
      if (singletonObject == null) {  
        throw ex;  
      }  
    } catch(BeanCreationException ex){  
      if (recordSuppressedExceptions) {  
        or (Exception suppressedException : this.suppressedExceptions) { 
          ex.addRelatedCause(suppressedException);  
      }  
    }  
      throw ex;  
    } finally{  
      if (recordSuppressedExceptions) {  
        this.suppressedExceptions = null;  
      } 
      // 单例bean创建完成后,容器移除bean
      afterSingletonCreation(beanName);  
    }  
    // newSingleton为true时,表示bean创建成功  
    // 判断是否为新的完成整bean
    if (newSingleton) {            
      // 将bean存入一级缓存中
      addSingleton(beanName, singletonObject);  
    }  
   } 
   return singletonObject;  
  }  
}

添加与清理缓存

csharp 复制代码
// 将bean放入一级缓存切清楚二级、三级缓存
protected void addSingleton(String beanName, Object singletonObject) {
  synchronized (this.singletonObjects) {xw
    // 添加到一级缓存中 
    this.singletonObjects.put(beanName, singletonObject); 
    // 从三级缓存中移除 
    this.singletonFactories.remove(beanName);
    // 从二级缓存中移除 
    this.earlySingletonObjects.remove(beanName);
    // 放入已注册的单利池中
    this.registeredSingletons.add(beanName);
  }
}
kotlin 复制代码
​
// 添加到三级缓存
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
  synchronized (this.singletonObjects) {
  // 若一级缓存不存在bean实例
  if (!this.singletonObjects.containsKey(beanName)) {
    // 添加到三级缓存
    this.singletonFactories.put(beanName, singletonFactory);
    // 从第二级缓存删除
    this.earlySingletonObjects.remove(beanName);
    // 放入已注册的单例池里
    this.registeredSingletons.add(beanName);
  }
}

4.总结

Spring 循环依赖是通过map缓存进行处理的,其中包括一级、二级、三级缓存,作用如下:

1、一级缓存SingletonObjects实例化、初始化实例。 2、二级缓存EarlySingletonObjects存放的是早期的 Bean ,半成品还未初始化的 bean。 3、三级缓存SingletonFactories是一个对象工厂,用于创建对象,然后放入到二级缓存中。同时对象如果存在 Aop 代理,那么返回的对象就是代理对象。

参考文献

www.51cto.com/article/702...

推荐阅读

化繁为简:Flutter组件依赖可视化

dubbo的SPI 机制与运用实现

埋点数据可视化的探索与实践

前端接口容灾

ARM架构下部署StarRocks3

招贤纳士

政采云技术团队(Zero),Base 杭州,一个富有激情和技术匠心精神的成长型团队。规模 500 人左右,在日常业务开发之外,还分别在云原生、区块链、人工智能、低代码平台、中间件、大数据、物料体系、工程平台、性能体验、可视化等领域进行技术探索和实践,推动并落地了一系列的内部技术产品,持续探索技术的新边界。此外,团队还纷纷投身社区建设,目前已经是 google flutter、scikit-learn、Apache Dubbo、Apache Rocketmq、Apache Pulsar、CNCF Dapr、Apache DolphinScheduler、alibaba Seata 等众多优秀开源社区的贡献者。

如果你想改变一直被事折腾,希望开始折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊......如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的技术团队的成长过程,我觉得我们该聊聊。任何时间,等着你写点什么,发给 zcy-tc@cai-inc.com

微信公众号

文章同步发布,政采云技术团队公众号,欢迎关注

相关推荐
pjw198809033 小时前
Spring Framework 中文官方文档
java·后端·spring
bropro7 小时前
【Spring Boot】Spring AOP中的环绕通知
spring boot·后端·spring
luom01027 小时前
springcloud springboot nacos版本对应
spring boot·spring·spring cloud
zuoerjinshu9 小时前
【spring专题】编译spring5.3源码
java·后端·spring
南昌彭于晏9 小时前
springcloud+openFeign单元测试解决初始化循环依赖的问题
spring·spring cloud·单元测试
SmartBrain11 小时前
FastAPI + LangGraph 与 SpringAI 在医疗场景应用及分析
人工智能·spring boot·spring·fastapi
Rsun0455111 小时前
Spring AI + RAG + 向量库 10 道模拟面试
人工智能·spring·面试
SmartBrain12 小时前
基于 Spring AI 构建多智能体协作系统(高级版)
java·人工智能·spring
Holen&&Beer13 小时前
Spring-Profile与部署说明
java·后端·spring
jinanmichael14 小时前
【SpringBoot】日志文件
java·spring boot·spring