Spring循环依赖解决方式源码解析
1. 什么是循环依赖?
循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于A我们直接上代码
先创建一个类ServiceA依赖于ServiceB,然后ServiceB又依赖于ServiceA
@Component
public class ServiceA {
@Autowired
private ServiceB serviceb;
@Component
public class ServiceB {
@Autowired
private ServiceA servicea;
2.Spring的单例对象的初始化主要分为三步:
createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象
填充属性,这一步主要是多bean的依赖属性进行填充
调用实现了InitializingBean的接口,实现afterPropertiesSet方法,或者调用在配置文件中同过init-method的方法。
从上面单例bean的初始化可以知道:循环依赖主要发生在第一、二步,也就是构造器循环依赖和field循环依赖。那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存。
这里会涉及到在spring内部所使用的两个内部属性,singletonFactories和earlySingletonObjects,这两个属性在类DefaultSingletonBeanRegistry中被定义,定义如下:
下面我们就看下spring创建单例bean的步骤吧
第一次创建ServiceA的步骤如下
org.springframework.beans.factory.support.AbstractBeanFactory#getBean方法在进入
由于是首次创建ServiceA在,singletonFactories和earlySingletonObjects和singletonFactories都不存在。所以返回了null。
然后通过反设创建了一个serviceA的BeanWrapper,然后在调用org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory这时候我们singletonFactories就存在serviceA了
紧接着就是调用populateBean填充ServiceA的属性了,由于ServiceA有属性ServiceB,此时我们的ServiceB还没有被加载,然后ServiceA有需要ServiceB于是spring又去调用了创建ServiceB的方法
于是我们接着继续填充ServiceB的属性,由于ServiceB又依赖ServiceA,于是再去创建ServiceA由于此时ServiceA已经在singletonFactories里面了,然后我们就返回了一个是ServiceA(此时的属性serviceB是为null的),并且ServiceA从singletonFactories里面移除掉加入到earlySingletonObjects。
这样做有什么好处呢?让我们来分析一下“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况。A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化。
知道了这个原理时候,肯定就知道为啥Spring不能解决“A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象”这类问题了!因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。
参考:https://blog.csdn.net/u010853261/article/details/77940767
Spring循环依赖解决方式源码解析的更多相关文章
- Spring里bean之间的循环依赖解决与源码解读
通过前几节的分析,已经成功将bean实例化,但是大家一定要将bean的实例化和完成bean的创建区分开,bean的实例化仅仅是获得了bean的实例,该bean仍在继续创建之中,之后在该bean实例的基 ...
- Session获取不到的情况及解决办法(源码解析)
本博客是自己在学习和工作途中的积累与总结,仅供自己参考,也欢迎大家转载,转载时请注明出处,请尊重他人努力成果,谢谢. 1. 当有连个sessionFactory时,容易产生获取不到session的情况 ...
- Spring AOP的实现及源码解析
在介绍AOP之前,想必很多人都听说AOP是基于动态代理和反射来实现的,那么在看AOP之前,你需要弄懂什么是动态代理和反射及它们又是如何实现的. 想了解JDK的动态代理及反射的实现和源码分析,请参见下面 ...
- Spring MVC工作原理及源码解析(三) HandlerMapping和HandlerAdapter实现原理及源码解析
1.HandlerMapping实现原理及源码解析 在前面讲解Spring MVC工作流程的时候我们说过,前端控制器收到请求后会调⽤处理器映射器(HandlerMapping),处理器映射器根据请求U ...
- 建议收藏!利用Spring解决循环依赖,深入源码给你讲明白!
前置知识 只有单例模式下的bean会通过三级缓存提前暴露来解决循环依赖的问题.而非单例的bean每次获取都会重新创建,并不会放入三级缓存,所以多实例的bean循环依赖问题不能解决. 首先需要明白处于各 ...
- SpringBoot 源码解析 (八)----- Spring Boot 精髓:事务源码解析
本篇来讲一下SpringBoot是怎么自动开启事务的,我们先来回顾一下以前SSM中是如何使用事务的 SSM使用事务 导入JDBC依赖包 众所周知,凡是需要跟数据库打交道的,基本上都要添加jdbc的依赖 ...
- 【SSH进阶之路】Spring的IOC逐层深入——源码解析之IoC的根本BeanFactory(五)
我们前面的三篇博文,简单易懂的介绍了为什么要使用IOC[实例讲解](二).和Spring的IOC原理[通俗解释](三)以及依赖注入的两种常用实现类型(四),这些都是刚开始学习Spring IoC容器时 ...
- Spring MVC工作原理及源码解析(二)DispatcherServlet实现原理及源码解析
1.DispatcherServlet 处理流程 从上一篇文章中Spring MVC原理图中我们可以看出:DispatcherServlet 在 Spring MVC框架 中处于核心位置,它负责协调和 ...
- 【spring源码学习】spring的事务管理的源码解析
[一]spring事务管理(1)spring的事务管理,是基于aop动态代理实现的.对目标对象生成代理对象,加入事务管理的核心拦截器==>org.springframework.transact ...
随机推荐
- The First Assignment
我的第一条随笔 ========== 这个作业属于哪个课程 https://edu.cnblogs.com/campus/zswxy/SE2020-2 这个作业要求在哪里 https://edu.cn ...
- nginx,wsgi项目部署
1.一些重要概念 https://www.cnblogs.com/xiaonq/p/8932266.html 1.1web容器 什么是web容器 1.web容器是帮助我们部署java丶php丶pyth ...
- 第8.10节 使用__class__查看Python中实例对应的类
一. 语法释义 __class__属性很简单,直接返回实例对应的类.语法如下: 实例. class 当不知道一个实例的类名又想对类的部分内容进行访问时可以使用__class__返回类. 注意:是返回实 ...
- HTTP请求头和响应头详解【转】
最近老猿在开始学习爬虫相关的知识,由于老猿以前只做非web的后台应用,发现相关知识太过匮乏,导致学习很困难,为此不得不从一些基础知识恶补开始,对于这些知识,老猿会将网上找到的比较认可的内容直接转发. ...
- PyQt(Python+Qt)学习随笔:Qt Designer中toolBar的allowedAreas属性
1.概述 allowedAreas属性指定工具栏允许移动的范围,其类型为枚举类Qt.ToolBarAreas,有如下取值: 以上取值可以同or操作组合使用. 2.访问方法 缺省值为Qt.AllTool ...
- Hello TLM
前言 目标 了解TLM程序的基本过程.TLM的英文全称是Transaction Level Modeling,中文翻译为事务级建模.它是在SystemC基础上的一个扩展库. 功能描述 模块A向模块B发 ...
- 学习笔记:四边形不等式优化 DP
定义 & 等价形式 四边形不等式是定义在整数集上的二元函数 \(w(x, y)\). 定义:对于任意 \(a \le b \le c \le d\),满足交叉小于等于包含(即 \(w(a, c ...
- Codeforces Edu Round 62 A-E
A. Detective Book 模拟题,有一些细节需要注意. #include <cstdio> #include <iostream> #include <cmat ...
- Android Studio/IDEA插件
1.android parcelable code generator 2.android code generator3.gson format4.android postfix completio ...
- hive实例的使用
一.hive用本地文件进行词频统计 1.准备本地txt文件 2.启动hadoop,启动hive 3.创建数据库,创建文本表 4.映射本地文件的数据到文本 5.hql语句进行词频统计交将结果保存到结果表 ...