Spring 循环依赖难点分析

目录

循环依赖和三级依赖

Spring 三级缓存和循环依赖(紧密相关)

循环依赖
  • 指的是两个或多个Bean之间相互依赖,如Bean A依赖于Bean B,而Bean B又依赖于Bean A,则会出现循环依赖的情况
  • Spring默认采用Singleton模式,Bean默认是单例的,在容器启动时就会创建Bean实例,同时也会注入它所依赖的Bean实例
  • 当出现循环依赖的情况时,Spring的Bean实例化策略就会出现问题
  • 大家开发过程中好像对循环依赖这个概念无感知,这种错觉是因为工作在Spring的中,已经帮你解决了
解决方案
  • Spring引入了"提前暴露Bean"的机制,在创建A对象时,会先创建一个A的空对象并将其添加到缓存池中
  • 即"提前暴露Bean",然后继续创建B对象,将其注入A对象中
  • 在创建B对象时,由于A对象已经在缓存池中,可以直接获取到A对象,接着将B对象注入到A对象中,完成Bean的初始化
  • 注意:循环依赖的场景
    • 单例
      • 构造器循环依赖(构造器注入,循环依赖无法解决,日常开发不推荐构造器注入)
      • Field属性循环依赖(set方法注入,循环依赖问题可以解决,通过三级缓存)
    • 多例
      • prototype类型bean(循环依赖无法解决,构造函数和属性注入都不能解决循环依赖)
      • AbstractBeanFactory#doGetBean
缓存说明
  • 三级缓存,当Spring创建Bean的过程中,使用3个缓存存储已经创建的Bean实例,Map结构
    • 一级缓存(成熟的bean) Singletons object cache
      • singletonObjects 单例池 ,存储已经创建好的无代理的单例Bean对象,从该缓存中取出的 bean 可以直接使用
    • 二级缓存 Early-singletons object cache
      • earlySingletonObjects 提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依
    • 三级缓存 Singleton Factory object cache
      • singletonFactories 单例对象工厂的cache,存放 bean 工厂对象,解决循环依赖 避免Bean的实例化顺序出现问题
总结
  • Spring的三级缓存机制是为了避免Spring中的循环依赖问题而引入的
  • 也可以提高Bean的创建效率,避免创建重复的对象,从而提高应用程序的性能

三级缓存流程解析

Bean的创建流程

循环依赖代码案例

java 复制代码
@Service
public class OneServiceImpl implements OneService {
    @Autowired
    private TwoService twoService;
    ...
}
java 复制代码
@Service
public class TwoServiceImpl implements TwoService {
    @Autowired
    private OneService oneService;
    ...
}

关键类 DefaultSingletonBeanRegistry

bash 复制代码
  /**
   * 一级缓存,单例池 ,存储已经创建好的无代理的单例Bean对象,从该缓存中取出的 bean 可以直接使用
   * */
 Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

  /**
   * 三级缓存, 单例对象工厂的cache,存放 bean 工厂对象,解决循环依赖 避免Bean的实例化顺序出现问题。
   * */
Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

  /**
   * 二级缓存, 提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依
   * */
Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

  /** 用于保存已经创建的 Bean 的单例对象,能够确保应用程序上下文中每个单例 Bean 对象最多只创建一次,并且可以保证单例 Bean 的唯一性 */
Set<String> registeredSingletons = new LinkedHashSet<>(256);

  /** 表示bean创建过程中都会存储这里,创建完成后会被移除 */
Set<String> singletonsCurrentlyInCreation =  Collections.newSetFromMap(new ConcurrentHashMap<>(16));


//add 关键方法 
//把创建的bean加到一级缓存中,然后把二级和三级缓存的bean移除
addSingleton(String beanName, Object singletonObject)

//三级缓存中存储,二级缓存中移除bean ,ObjectFactory是函数式接口
addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)

//get关键方法
//根据bean名称获取对应的bean,如果不存在则创建新的,ObjectFactory是调用外部传递进来的lambda表达式
getSingleton(String beanName, ObjectFactory<?> singletonFactory)

//用于从单例池中获取指定名称的单例 Bean 实例,方法内部实现了三级缓存查找机制
getSingleton(String beanName, boolean allowEarlyReference)

关键类 ObjectFactory

bash 复制代码
/**
 * 将创建对象的步骤封装到ObjectFactory中, 外部通过lambda表达式把相关创建步骤传递进来,返回创建好的对象
 /
@FunctionalInterface
public interface ObjectFactory<T> {
  T getObject() throws BeansException;
}

循环依赖流程图

java 复制代码
// 1、bean构建完毕,从singletonsCurrentlyIncreation集合中删除对应的beanName
afterSingletonCreation(beanName);
 
// 2、添加到单例缓存池中,并从二级缓存和三级缓存中删除
addSingleton(beanName, singletonObject);
java 复制代码
	// 添加到单例缓存池中,并从二级缓存和三级缓存中删除
	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, singletonObject);
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

补充-Spring循环依赖异常

相关推荐
海边的Kurisu1 小时前
苍穹外卖日记 | Day1 苍穹外卖概述、开发环境搭建、接口文档
java
uzong4 小时前
后端线上发布计划模板
后端
C雨后彩虹5 小时前
任务最优调度
java·数据结构·算法·华为·面试
uzong5 小时前
软件工程师应该关注的几种 UML 图
后端
heartbeat..5 小时前
Spring AOP 全面详解(通俗易懂 + 核心知识点 + 完整案例)
java·数据库·spring·aop
Jing_jing_X5 小时前
AI分析不同阶层思维 二:Spring 的事务在什么情况下会失效?
java·spring·架构·提升·薪资
上进小菜猪6 小时前
基于 YOLOv8 的 100 类中药材智能识别实战 [目标检测完整源码]
后端
元Y亨H7 小时前
Nacos - 服务发现
java·微服务
微露清风7 小时前
系统性学习C++-第十八讲-封装红黑树实现myset与mymap
java·c++·学习
dasi02277 小时前
Java趣闻
java