简单说说 spring 是如何处理循环依赖问题的(源码解析)

embedded/2024/10/18 17:17:16/

聊聊源码

spring 中,解决循环依赖的关键是三级缓存,缓存数据在 DefaultSingletonBeanRegistry类中

/** Cache of singleton objects: bean name to bean instance. */
//一级缓存,是最终生成的对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);/** Cache of singleton factories: bean name to ObjectFactory. */
//三级缓存,和aop相关
//ObjectFactory 是一个函数式接口,如果有进行aop,则返回aop对象,否则返回的是原始对象
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);/** Cache of early singleton objects: bean name to bean instance. */
//二级缓存,完成实例化,未完成初始化的对象
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

1 提前暴露

AbstractAutowireCapableBeanFactory#doCreateBean

在这个方法中,spring会将已经完成实例化的bean放入到三级缓存中去。

java">//AbstractAutowireCapableBeanFactory 类
//创建bean的过程
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.BeanWrapper instanceWrapper = null;//如果是单例,先从缓存中进行获取,如果获取的到,则直接使用if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}//如果缓存中没有,则创建bean实例if (instanceWrapper == null) {//【核心】创建实例instanceWrapper = createBeanInstance(beanName, mbd, args);}.... 其他处理逻辑/******************************* 1 提前暴露 *******************************/// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.//判断是否支持循环依赖(是单例 && 允许循环引用 && 当前bean正在创建中)boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}//这个方法会将当前的实例化完成,初始化未完的对象,存储到 singletonFactories 缓存中去//之所以存到的是三级缓存,是因为对象可能是经过aop的,在singletonFactories中存储的是ObjectFactory// ObjectFactory是一个函数式接口,如果对象有经过aop会返回aop对象,否则返回原对象//singletonFactories 就是我们所说的三级缓存//同时,会将beanName 存入 registeredSingletons 列表中,作用是提前曝光,是解决循环袭来的关键addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// Initialize the bean instance.Object exposedObject = bean;try {//【核心】填充bean的属性//1 完成属性的注入,根据bean定义的时候,设置的属性值。//2 解析依赖 如果bean依赖其他的bean,则其会递归的去创建其他的bean//3 调用BeanPostProcessor,这里主要是执行 InstantiationAwareBeanPostProcessorpopulateBean(beanName, mbd, instanceWrapper);//【核心】初始化beanexposedObject = initializeBean(beanName, exposedObject, mbd);}... 其他处理逻辑return exposedObject;
}

2 从缓存中依次获取bean

在populateBean中,会去循环遍历需要进行依赖注入的bean,会通过getBean方法去进行获取,会走到DefaultSingletonBeanRegistry.getSingleton

过程如下

1 从一级缓存中获取,如果可以获取到,直接返回

2 如果一级缓存中获取不到,从二级缓存中获取,如果获取到,则直接返回

3 如果二级缓存也获取不到,则从三级缓存中获取

java">protected Object getSingleton(String beanName, boolean allowEarlyReference) {// Quick check for existing instance without full singleton lock//先从一级缓存中获取,一级缓存存放的是成品bean,就是已经完成了实例化和初始化Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {//从二级缓存中获取(早期单例缓存),只完成了实例化的beansingletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// Consistent creation of early reference within full singleton lock//这里进行二重检查singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {// 从三级缓存中获取,三级缓存存放的是ObjectFactory,// ObjectFactory是一个函数式接口,这个方法就是 // AbstractAutowireCapableBeanFactory.getEarlyBeanReferenceObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;
}

三级缓存中存储的对象是ObjectFactory,这个对象定义的是一个函数式接口,最终会调用回设置的位置,就是

AbstractAutowireCapableBeanFactory的getEarlyBeanReference方法

java">protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {//这是一个BeanPosterProcessor,是一个接口//1 允许获取一个早期的bean//2 一般框架中,比如mybatis中的事务,会实现这个接口的getEarlyBeanReference方法//然后返回一个aop之后的类。SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);}}}return exposedObject;
}

总结

从源码中,我们可以推断出,循环依赖的处理有几个基础的点,分别是

1 三级缓存,三级缓存中的第三级缓存是处理循环依赖的关键

2 提前暴露,在bean完成实例化之后,spring就会将还未创建完成的bean暴露到三级缓存中。

3 允许获取仅实例化的bean进行赋值。

需要注意的是:spring中,只能实现set注入的循环依赖,如果在构造器中产生循环依赖,spring无法解决

传送阵

简单说说 spring构造器循环依赖 为什么无法解决(源码解析)-CSDN博客


http://www.ppmy.cn/embedded/128515.html

相关文章

数据结构——树和森林

目录 树的存储结构 1、双亲表示法 2、孩子链表 3、孩子兄弟表示法 树与二叉树的转换 将树转换为二叉树 将二叉树转换为树 森林与二叉树的转化 森林转换成二叉树 二叉树转换为森林 树和森林的遍历 1、 树的遍历&#xff08;三种方式&#xff09; 2、森林的遍历 树的存…

linux的学习第二天

1.vmware的功能&#xff1a; 快照 创建快照&#xff1a; 拍摄此虚拟机的快照&#xff1a;记录保存虚拟机的当前状态&#xff0c;如果系统出现故障&#xff0c;可以通过快照还原&#xff08;错删系统时可以找到快照的系统状态&#xff0c;然后恢复系统&#xff09; 恢复快照…

2024.10月11日--- SpringMVC拦截器

拦截器 1 回顾过滤器&#xff1a; Servlet规范中的三大接口&#xff1a;Servlet接口&#xff0c;Filter接口、Listener接口。 过滤器接口&#xff0c;是Servlet2.3版本以来&#xff0c;定义的一种小型的&#xff0c;可插拔的Web组件&#xff0c;可以用来拦截和处理Servlet容…

计算机毕设选题推荐【大数据专业】

计算机毕设选题推荐【大数据专业】 大数据专业的毕业设计需要结合数据的采集、存储、处理与分析等方面的技能。为帮助同学们找到一个适合且具有实践性的选题&#xff0c;我们为大家整理了50个精选的毕设选题。这些选题涵盖了大数据分析、处理技术、可视化等多个方向&#xff0…

问:JVM当中的垃圾分类怎么搞?

在Java中&#xff0c;JVM&#xff08;Java虚拟机&#xff09;的垃圾识别与分类是自动内存管理的重要组成部分。这一过程主要通过垃圾收集器&#xff08;Garbage Collector&#xff09;实现&#xff0c;旨在识别和回收不再被程序引用的对象&#xff0c;以释放内存空间。 1. 垃圾…

Python网络爬虫入门指南

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

【分布式训练(5)】无法 kill PID?如何 kill 休眠中的 GPU 占用进程

【分布式训练 debug】VS Code Debug 技巧&#xff1a;launch.json实用参数 【分布式训练&#xff08;2&#xff09;】深入理解 DeepSpeed 的 ZeRO 内存优化策略 (三阶段的区别) 【分布式训练&#xff08;3&#xff09;】accelerator deepspeed debug 报错 “Timed out waiting…

《Python基础教程》第一章笔记

硬件环境&#xff1a;阿里云99计划云主机&#xff08;99元包年&#xff09; 操作系统&#xff1a;Ubuntu 22.04 代码仓库&#xff1a;https://gitee.com/zustzjut/BeginningPython 第1章 快速上手&#xff1a;基础知识 1.1 交互式解释器 如果你使用的是macOS或Linux&#xff…