Spring源码--debug分析循环依赖--构造器注入
目的:源码调试构造器注入,看看是怎么报错的。
spring:5.2.3
jdk:1.8
一、准备
首先准备两个循环依赖的类:userService和roleServic
<bean id="userService" class="com.chris.spring.service.UserServiceImpl">
<constructor-arg ref="roleService"/>
</bean>
<bean id="roleService" class="com.chris.spring.service.RoleService">
<constructor-arg ref="userService"/>
</bean>
二、开始调试
因为依赖注入的触发点是容器初始化所有非懒加载Bean时候,所以可以直接refresh()方法中的finishBeanFactoryInitialization(beanFactory);方法看起。
1 /**
2 * Finish the initialization of this context's bean factory,
3 * initializing all remaining singleton beans.
4 */
5 protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
......
//初始化所有非懒加载的单例bean,
32 // Instantiate all remaining (non-lazy-init) singletons.
33 beanFactory.preInstantiateSingletons();
34
1.获取BeanDefinition集合
说明:可以看到userService和roleServic已经在容器中,准备初始化了。继续:
2. 判断是否需要实例化
- 循环遍历每个BeanDefinition,判读是不是 非抽象的&&单例的&&懒加载的 bean,不是跳过。
- 判断是不是工厂bean,是工厂bean,走处理工厂的方法。
- 不是工厂bean,是普通bean,初始化,继续向下


getSingleton(beanName);作用:从缓存中获取单例Bean。

singletonObjects用于缓存单例对象。从singletonObjects获取userService。由于是第一次获取,里面肯定没有。

调用isSingletonCurrentlyInCreation(beanName)方法,判断是不是在初始化中:

singletonsCurrentlyInCreation用于存储正在初始化的Bean。正在初始化的集合里为空,返回false

- isSingletonCurrentlyInCreation(beanName)返回false。
- getSingleton(beanName);没有获取到已经初始化的bean,返回null。

- 没有获得单例bean,走else方法
- else代码块中,上面一堆判断不重要,直接看下面。markBeanAsCreated方法标记这个bean,准备好创建了。


alreadyCreated用户存放准备好初始化的Bean.
回到原来的方法,又是略过一段代码,判断userService是不是单例bean,肯定是,走这个方法。

- 是单例bean,走这个分支。
- 又是一个getSingleton方法,注意:这个是方法是两个参数,一个是beanName,一个是ObjectFactory<?>对象工厂,如下图:

- 首先看一下注释:返回给定名字的单例对象,如果没有注册过,那么就创建并注册。注册是什么??留一个疑问。
- ObjectFactory<?>是什么?它就是一个简单工厂,可以返回对象实例,怎么用,下面说。

- this.singletonObjects,又是它:缓存单例对象的集合。上面获取一遍,没有,这次还是没有。
- 在创建单例bean之前要做些事情:检查这个bean是否可以被创建,逻辑如下:

- inCreationCheckExclusions:要排除的bean的集合。userService不是要排除的bean,这个判断是true。
- singletonsCurrentlyInCreation:正在创建的单例集合。把userService放在要被创建的集合,插入成功是true,前面还有一个‘!’,所以这个判断是false。不抛出异常。看到这里基本可以明白个大概了,循环依赖就是这里抛出的异常:二次检查userService时,userService在singletonsCurrentlyInCreation,说明userService正准备初始化,已经循环依赖了。可继续往下看:

检查没问题,userService可以被创建,通过工厂获取对象。从ObjectFactory<?>这个对象工厂中获取bean:singletonFactory.getObject();
问题来了,这个工厂(singletonFactory)哪来的?怎么工作的?

它是传进来的参数,怎么传进来的,再看看调用:

原来是createBean(beanName, mbd, args);返回的。那么就是说明createBean会返回一个工厂对象,这个工厂对象只生产userService这一个对象。
点进createBean方法,找到doCreateBean方法


- 在这个类里。
- 看注释,实际创建Bean的方法。跟踪调试看一下:

- 先从缓存中去掉,
- 再创建一个新的,点进去

将BeanDefinition解析成Class,忽略上面这个图停留的地方,继续向下:

判断是构造器注入的,点进去:

这里有两个方法,走第二个,构造器解析:

- 在构造器解析类中
- 解析userService中有一个构造器
- 构造器参数类型是RoleService,继续向下走:

解析构造器参数,点进去:

解析构造器参数的值,即参数名称:
解析出来是roleService。容器会先将依赖的bean初始化好。然后关联起来。

从这里开始,又是一顿分析roleSerice,过程和userService一样。分析到的结果是:roleSerice依赖userService。又去获取userService:

这个胡汉三又回来了!
然后又是getSingleton(),往下看:



现在准备初始化的bean集合有两个了,而且就有userService,下面看容器时怎么报错的:
又是往下走:到了这里

又检查userService时候可以初始化:

准备初始化的集合里面有userService,报错了!
Spring源码--debug分析循环依赖--构造器注入的更多相关文章
- Spring源码-IOC部分-循环依赖-用实例证明去掉二级缓存会出现什么问题【7】
实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...
- Spring源码解析-基于注解依赖注入
在spring2.5版本提供了注解的依赖注入功能,可以减少对xml配置. 主要使用的是 AnnotationConfigApplicationContext: 一个注解配置上下文 AutowiredA ...
- 再探循环依赖 → Spring 是如何判定原型循环依赖和构造方法循环依赖的?
开心一刻 一天,侄子和我哥聊天,我坐在旁边听着 侄子:爸爸,你爱我妈妈吗? 哥:这话说的,不爱能有你吗? 侄子:确定有我不是因为荷尔蒙吗? 哥:因为什么荷尔蒙,因为爱情! 侄子:那我妈花点钱,你咋老说 ...
- Spring源码分析(十七)循环依赖
本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 实例化bean是一个非常复杂的过程,而其中比较难以理解的就是对循环依赖的解决, ...
- Spring 的循环依赖,源码详细分析 → 真的非要三级缓存吗
开心一刻 吃完晚饭,坐在院子里和父亲聊天 父亲:你有什么人生追求? 我:金钱和美女 父亲对着我的头就是一丁弓,说道:小小年纪,怎么这么庸俗,重说一次 我:事业与爱情 父亲赞赏的摸了我的头,说道:嗯嗯, ...
- Spring源码学习笔记9——构造器注入及其循环依赖
Spring源码学习笔记9--构造器注入及其循环依赖 一丶前言 前面我们分析了spring基于字段的和基于set方法注入的原理,但是没有分析第二常用的注入方式(构造器注入)(第一常用字段注入),并且在 ...
- Spring IOC 容器源码分析 - 循环依赖的解决办法
1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...
- Spring源码分析之循环依赖及解决方案
Spring源码分析之循环依赖及解决方案 往期文章: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostPro ...
- Spring源码解析(五)循环依赖问题
引言 循环依赖就是多个类之间互相依赖,比如A依赖B,B也依赖A,如果日常开发中我们用new的方式创建对象,这种循环依赖就会导致不断的在创建对象,导致内存溢出. Spring是怎么解决循环依赖的问题的? ...
随机推荐
- Spring Framework 5.0简述
从Spring框架5.0开始,Spring需要JDK 8+ (Java SE 8+),并且已经为JDK 9提供了现成的支持. Spring框架还支持依赖注入(JSR 330)和通用注释(JSR 250 ...
- 【鸿蒙应用开发】第三章 “颜控”时代下如何构建UI界面
为什么是第三章,前面两章呢? 原本是以碎片化的方式将HarmonyOS应用开发快速掌握,但是在准备六大布局组合复杂UI界面Demo时,很多组件之前都没有应用.因此准备将知识体系进行细化,以章节的形式希 ...
- 利用python库stats进行t检验
t检验通常分为三种,分别是单样本t检验.双样本t检验和配对样本t检验.本文基于python的scipy.stats函数对每种t检验进行了介绍和实验. 一.t检验介绍 无论哪种t检验,都有以下的基本 ...
- 第14.4节 使用IE浏览器获取网站访问的http信息
上节<第14.3节 使用google浏览器获取网站访问的http信息>中介绍了使用Google浏览器怎么获取网站访问的http相关报文信息,本节介绍IE浏览器中怎么获取相关信息.以上节为基 ...
- 【Azure Redis 缓存 Azure Cache For Redis】Azure Redis由低级别(C)升级到高级别(P)的步骤和注意事项, 及对用户现有应用的潜在影响,是否需要停机时间窗口,以及这个时间窗口需要多少的预估问题
问题描述 由于Azure Redis的性能在不同级别表现不同,当需要升级/缩放Redis的时候,从使用者的角度,需要知道有那些步骤? 注意事项? 潜在影响?停机事件窗口? 升级预估时间? 解决方案 从 ...
- Day4 Scrum 冲刺博客
线上会议: 昨天已完成的工作与今天计划完成的工作及工作中遇到的困难: 成员姓名 昨天完成工作 今天计划完成的工作 工作中遇到的困难 纪昂学 创建一个Tetromino类来作为7个经典形状的父类 绘制下 ...
- HTML引入外部字体
HTML5如何引入外部字体 背景 现在我需要 "Montserrat-ExtraLight ExtraLight"类型的字体,但是html的font-family中找不到这个类型的 ...
- Panda 交易所视点观察!区块链金融应用迎新规,哪些版块受影响?
Panda交易所获悉,近日央行下发推动<区块链技术规范应用的通知>(以下简称"通知")及<区块链技术金融应用评估规则>(以下简称"规则" ...
- P4317 花神的数论题,关于luogu题解粉兔做法的理解
link 题意 设 \(\text{sum}(i)\) 表示 \(i\) 的二进制表示中 \(1\) 的个数.给出一个正整数 \(N\) ,求 \(\prod_{i=1}^{N}\text{sum}( ...
- 2020中国.NET开发者峰会近50场热点技术专题揭秘
简介 / Summary 2014年微软组织并成立.NET基金会,微软在成为主要的开源参与者的道路上又前进了一步.2014年以来已经有众多知名公司加入.NET基金会,微软,Google,AWS三大云厂 ...

