服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - Spring如何使用三级缓存解决循环依赖

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

2023-12-13 14:44政采云技术 Java教程

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

1. 前言

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

2. 循环依赖简介

2.1 什么是循环依赖

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

Spring如何使用三级缓存解决循环依赖图片

public class PersonA { 
  @Autowired
  private PersonB personB;
}
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 放入到一级缓存中

/** 一级缓存 */
private final Map singletonObjects = new ConcurrentHashMap(256);
/** 二级缓存 */ 
private final Map earlySingletonObjects = new HashMap(16); 
/** 三级缓存 */ 
private final Map> singletonFactories = new HashMap>(16);

3.2 三级缓存流程

Spring如何使用三级缓存解决循环依赖图片

3.3 三级缓存源码解析

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

protected  T doGetBean( String name, @Nullable Class 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()

// 这个方法主要是三级缓存容器,思路大概是:从一级缓存查询,若找不到去二级缓存查询,还是不存在则去三级缓存,若三级缓存找到了,则将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;
}
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;  
  }  
}

添加与清理缓存

// 将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);
  }
}
// 添加到三级缓存
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缓存进行处理的,其中包括一级、二级、三级缓存,作用如下:

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

参考文献

https://www.51cto.com/article/702892.html

原文地址:https://mp.weixin.qq.com/s/KHBmavlqQCBaKwPzGbK4oQ

延伸 · 阅读

精彩推荐
  • Java教程一篇文章带你搞定JAVA泛型

    一篇文章带你搞定JAVA泛型

    泛型是Java中的高级概念,也是构建框架必备技能,比如各种集合类都是泛型实现的,今天详细聊聊Java中的泛型概念,希望有所收获...

    香菜聊游戏3892021-10-08
  • Java教程mybatis if标签判断不生效的解决方法

    mybatis if标签判断不生效的解决方法

    这篇文章主要介绍了mybatis if标签判断不生效的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友...

    暮霭层层楚天阔12242021-08-05
  • Java教程java 中RSA的方式实现非对称加密的实例

    java 中RSA的方式实现非对称加密的实例

    这篇文章主要介绍了java 中RSA的方式实现非对称加密的实例的相关资料,这里提供实例帮助大家理解这部分知识,需要的朋友可以参考下...

    Sahadev_4362020-12-11
  • Java教程Java实现的zip压缩及解压缩工具类示例

    Java实现的zip压缩及解压缩工具类示例

    这篇文章主要介绍了Java实现的zip压缩及解压缩工具类,结合实例形式分析了java对文件的进行zip压缩及解压缩的具体操作技巧,需要的朋友可以参考下...

    soundfly11222021-03-18
  • Java教程Spring Security整合CAS的示例代码

    Spring Security整合CAS的示例代码

    本篇文章主要介绍了Spring Security整合CAS的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    乱世浮生7072021-05-13
  • Java教程java 中sleep() 和 wait() 的对比

    java 中sleep() 和 wait() 的对比

    这篇文章主要介绍了java 中sleep() 和 wait() 的对比的相关资料,需要的朋友可以参考下...

    xyh2693922020-09-06
  • Java教程动态代理模拟实现aop的示例

    动态代理模拟实现aop的示例

    下面小编就为大家带来一篇动态代理模拟实现aop的示例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧,希望对大...

    C_凯9162021-01-30
  • Java教程浅谈springboot @Repository与@Mapper的区别

    浅谈springboot @Repository与@Mapper的区别

    本文主要介绍了浅谈springboot @Repository与@Mapper的区别,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    轻舟渡沧海7712022-09-14