简单易懂又非常牛逼的Spring源码解析,推断构造与bean的实例化
简单易懂又非常牛逼的Spring源码解析,推断构造与bean的实例化
- 原理解析
- 实例化bean的入口
- 工厂方法实例化
- 推断构造
- 初次筛选
- 二次筛选
- bean的实例化
- 代码走读
- 实例化bean的入口
- createBeanInstance方法内部的流程
- 推断构造
- 初次筛选
- 二次筛选
- bean的实例化
- 总结
往期文章:
- 人人都能看懂的Spring底层原理,看完绝对不会懵逼
- 简单易懂的Spring扩展点详细解析,看不懂你来打我
- 人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册
- 简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑
上一篇文章讲到了ConfigurationClassPostProcessor的具体逻辑,至此所有的配置信息已经被解析成BeanDefinition,注册到容器中了。
下一步就是根据BeanDefinition进行非懒加载的单例bean的实例化。因此,本篇文件就要介绍非懒加载单例bean的实例化过程。
原理解析
实例化bean的入口
要研究bean的实例化,首先就要找到bean的实例化的入口。
- Spring首先会进行配置文件或者配置类的解析,然后生成BeanDefinition并注册到容器中
- 待所有的BeanDefinition都注册完毕后,会遍历每一个BeanDefinition,判断是否是非抽象、单例、非懒加载,如果是,则调用getBean(beanName)方法进行bean的预加载
- 然后会尝试从容器中获取,看是否已经初始化完成,如果没有,则进行bean的实例化
- 而bean的实例化的入口,就是createBeanInstance方法
这个createBeanInstance方法,就是我们这篇文件研究的主体。
工厂方法实例化
bean的实例化有两种方式,一种是工厂方法实例化,一种是构造器实例化。
createBeanInstance方法,首先会检查BeanDefinition中的factoryMethodName属性是否不为空,如果不为空,就会使用工厂方法进行实例化。
我们上一篇文章分析的@Bean注解修饰的方法,生成的BeanDefinition就带有factoryMethodName属性。
如果BeanDefinition中的factoryMethodName属性为空,就要通过构造器进行实例化。
推断构造
构造器实例化,先要进行构造器的挑选。
如果要通过构造器进行实例化的话,就要先挑选一个合适的构造器,因为一个类里面构造器可能有多个。
推断构造:
- 第一步初次筛选,返回一个构造器数组
- 初次筛选返回的构造器数组不为空,或者自动注入模型为构造器注入,或者我们指定了构造器参数,则进行二次筛选,挑出一个最合适的
- 否则走最简单的逻辑,使用无参构造器进行实例化
初次筛选
初次筛选的条件如下,符合条件的构造器,会放到一个数组里面返回。
二次筛选
二次筛选就是从初次筛选的结果中,再筛出一个最合适的。
这里主要就是筛选出要使用的构造器,以及构造器要使用的参数,筛选出的构造器会保存到constructorToUse变量,而构造器要使用的参数则保存到argsToUse变量。
上面这张图就是推断构造二次筛选的流程,比较复杂,这里看不懂的可以忽略,非重点。
- 首先会判断我们是否手动传入了参数,如果是,则使用我们手动传的参数
- 如果我们没有手动传参,则看BeanDefinition中是否缓存了曾经筛选出的构造器和参数,如果是,则使用缓存的
- 如果BeanDefinition没有缓存,看是否只有一个无参构造器,如果是,则使用该构造器,并缓存到BeanDefinition中
- 如果有多个构造器,先确定最少需要的参数个数minNrOfArgs(取值情况看上图)
- 然后对构造器进行排序(排序条件看上图)
- 遍历构造器数组
- 如果已经挑选出最合适的构造器,当前遍历到的构造器需要的参数又更少,则break跳出循环
- 如果当前遍历到的构造器需要的参数个数,少于minNrOfArgs,continue跳过该构造器
- 没有break,也没有continue,那么就要获取当前构造器需要的参数(如何获取看上图)
- 然后计算取得的参数个数和 构造器所需参数间的差值
- 跟之前选出的构造器进行比较,保留差值更小的构造器
- 遍历结束
- 如果不是主动传参的情况,则把筛选结果缓存到BeanDefinition
bean的实例化
经过上面的步骤,现在已经挑选出合适的构造器,或者使用无参构造器。接下来就是要通过构造器,进行反射实例化。
Spring通过InstantiationStrategy(实例化策略),进行bean实例化的。
也就是说,不是直接通过构造器进行反射实例化的,而是再包了一层,提供了一定的可扩展性。我们可以实现自己的InstantiationStrategy,在bean实例化的时候做一些特殊处理。
如果我们没有做特殊处理,那么Spring就会使用SimpleInstantiationStrategy(简单实例化策略),里面才是通过构造器进行反射实例化。
然后会把实例化的bean包装在一个BeanWrapper中返回。
代码走读
上面已经对bean实例化的整个过程介绍完毕,下面是通过代码走读对上面的原理分析进行验证。
实例化bean的入口
首先是在代码中找到bean实例化的入口
验证上面的路径一路跟进去,就到了实例化bean的入口,createBeanInstance方法。
createBeanInstance方法内部的流程
进入createBeanInstance方法内部,可以看到推断构造和实例化的流程。
判断BeanDefinition的factoryMethodName属性是否不为空,如果不为空,则使用工厂方法进行实例化。
如果我们没有主动传参,并且BeanDefinition中的resolvedConstructorOrFactoryMethod为true,resolved变量置为true,表示之前已经处理过,下面不需要再进行推断构造的处理。autowireNecessary变量表示是否需要进行构造器的自动注入,也就是是否是一个有参构造。
之前已经处理过,就直接进行实例化,不需要再次进行构造器的推断。上面是带参构造的实例化,下面是无参构造的实例化。
如果之前没有处理过,就要进行推断构造。determineConstructorsFromBeanPostProcessors方法就是推断构造的初次筛选,返回一个构造器数组。
初次筛选返回构造器数组后,如果构造器数组不为空,或者当前BeanDefinition的自动注入模型是构造器注入,或者我们在配置中指定了构造器参数,或者我们主动传递了参数,只有满足这四个条件中的一个,就会调用autowireConstructor方法,里面就包含了二次筛选的逻辑。
四个条件没一个满足,就走最简单的逻辑,使用无参构造器进行实例化。
推断构造
再来看一下推断构造的代码。
初次筛选
determineConstructorsFromBeanPostProcessors方法,就是初次筛选的逻辑。
推断构造的初次筛选,是通过bean后置处理器进行的。如果我们打断点的话,会发现进入到AutowiredAnnotationBeanPostProcessor里面。
双重检测锁,检测缓存中有没有。
缓存中没有,就继续往下走。先取出所有构造器。
声明四个变量:
- candidates,候选构造器集合,除无参构造外,遍历到的构造器,只要不报错,都会往里面放
- requiredConstructor,@Autowired或@Autowired(required = true)注解修饰的构造器,@Autowired注解的required属性默认为true
- defaultConstructor,无参构造器
- primaryConstructor,直接忽略它,非Kotlin代码就是个null
遍历所有的构造器,寻找构造器上的@Autowired注解。
如果构造器带有@Autowired注解,但是之前已经有一个@Autowired修饰的构造器,那么直接报错。
如果构造器上的@Autowired注解的required属性是true(默认就是true),然后又有其他构造器,也直接报错。
都没有报错,代表目前为止只有为一个并且是@Autowired注解修饰的构造器,赋值给requiredConstructor遍历,并且添加到candidates中。
如果构造器没有@Autowired注解,并且是无参构造器,赋值给defaultConstructor。
然后所有构造器都遍历完毕后,进入下面的逻辑。
如果candidates集合不为空,requiredConstructor为空,defaultConstructor不为空,把defaultConstructor添加到candidates集合。也就是没有@Autowired或者@Autowired(required = true)注解修饰的构造器,只有不带@Autowired注解的构造器或者@Autowired(required = false)注解修饰的构造器,那么把无参构造器添加到candidates集合中,作为返回集。
如果只有一个构造器,并且是一个带参构造,则返回这个构造器。
下面两个else if分支必须primaryConstructor不为null才会进去,但是primaryConstructor是不可能不为null的,所以直接忽略。
剩下一个else分支,返回一个空的构造器数组。
二次筛选
autowireConstructor方法里面,包含了二次筛选的逻辑,也就是从构造器数组中,挑出一个最合适的构造器。
然后里面会new一个ConstructorResolver并调用它的autowireConstructor方法。
两个重要变量,constructorToUse(准备使用的构造器)和argsToUse(准备使用的参数)。
如果我们传递了参数,argsToUse赋值为我们传递的参数。
尝试从缓存中获取之前处理过的构造器和参数。
如果缓存中没有,看是否只有一个构造器,并且我们没有传递参数,而且配置中也没有指定构造参数。如果是,那么把无参构造和空参缓存到BeanDefinition中,然后通过无参构造器进行实例化。
minNrOfArgs,最少需要的参数个数。如果我们传递了参数,就是我们传递的参数个数,如果我们没有传递参数,那么看我们是否在配置中指定了构造器参数,如果指定了,那么就是我们指定的构造器参数个数,否则就是0。
对构造器进行排序。
遍历构造器,如果constructorToUse(准备使用的构造器)和argsToUse(准备使用的参数)不为空,而且当前遍历到的构造器参数个数parameterCount少于argsToUse的长度,那么直接break,后面的都不用再遍历了。
如果当前构造器参数个数parameterCount少于minNrOfArgs(最少需要的参数个数),那么continue忽略当前构造器。
获取构造器所需要的参数。
计算取得的参数个数和构造器所需要的参数个数的差值typeDiffWeight,如果差值比之前的还小,就选择当前的构造器,更新constructorToUse和argsToUse,以及minTypeDiffWeight(目前找到的最小参数个数差值)。
如果我们没有传递参数,则把处理结果缓存到BeanDefinition中。然后使用推断出的构造器constructorToUse和构造参数argsToUse进行反射实例化。
bean的实例化
坚持就是胜利,剩下的就是实例化bean的逻辑!
instantiate(beanName, mbd, constructorToUse, argsToUse),使用推断出的构造器constructorToUse和构造参数argsToUse进行反射实例化。
strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse),调用了InstantiationStrategy(实例化策略)的instantiate方法。
调用了BeanUtils工具类的instantiateClass方法。
最终通过构造器反射实例化。
还有一个使用无参构造进行实例化的方法,也是差不多的逻辑。
getInstantiationStrategy().instantiate(mbd, beanName, parent)也是通过InstantiationStrategy(实例化策略)的instantiate方法进行实例化。
然后可以看到包装成一个BeanWrapper返回。
getInstantiationStrategy().instantiate(mbd, beanName, parent)里面,也是通过BeanUtils工具类的instantiateClass方法进行实例化。
总结
以上就是推断构造与bean的实例化源码解析的所有内容,下面做一个总结。
- 推断构造首先会经过第一次筛选,筛出一个构造器数组
- 然后构造器数组不为空,或者当前BeanDefinition指定为构造器注入,或者我们在配置中指定了构造器参数,或者我们getBean的时候传递了参数,那么就会经历第二次筛选,选出一个最合适的构造器以及构造器参数
- 否则就会使用无参构造器进行实例化
- 然后会通过InstantiationStrategy(实例化策略)进行实例化,里面调用BeanUtils工具类,最终通过构造器进行反射实例化
相关文章:
简单易懂又非常牛逼的Spring源码解析,推断构造与bean的实例化
简单易懂又非常牛逼的Spring源码解析,推断构造与bean的实例化原理解析实例化bean的入口工厂方法实例化推断构造初次筛选二次筛选bean的实例化代码走读实例化bean的入口createBeanInstance方法内部的流程推断构造初次筛选二次筛选bean的实例化总结往期文章࿱…...
Win11的两个实用技巧系列清理磁盘碎片、设置系统还原点的方法
Win11如何清理磁盘碎片?Win11清理磁盘碎片的方法磁盘碎片过多,会影响电脑的运行速度,所以需要定期清理,这篇文章将以Win11为例,给大家分享的整理磁盘碎片方法相信很多用户都会发现,随着电脑使用时间的增加,…...
嵌入式 STM32 红外遥控
目录 红外遥控 NEC码的位定义 硬件设计 软件设计 源码程序 红外遥控 红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,容易实现等显著的特点,被诸多电子设备特别…...
【java web篇】使用JDBC操作数据库
📋 个人简介 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜📝 个人主页:馆主阿牛🔥🎉 支持我:点赞👍收藏⭐️留言Ὅ…...
华为OD机试题,用 Java 解【最小步骤数】问题
最近更新的博客 华为OD机试题,用 Java 解【停车场车辆统计】问题华为OD机试题,用 Java 解【字符串变换最小字符串】问题华为OD机试题,用 Java 解【计算最大乘积】问题华为OD机试题,用 Java 解【DNA 序列】问题华为OD机试 - 组成最大数(Java) | 机试题算法思路 【2023】使…...
JAVA中 throw 和 throws 的区别含案例
JAVA中 throw 和 throws 的区别含案例 在 Java 中,throw 和 throws 是两个关键字,它们用于处理异常。 throw 关键字用于抛出一个异常对象。一旦抛出异常,程序将停止执行当前方法的剩余代码,并尝试寻找与该异常匹配的 catch 块来…...
基于SpringCloud的可靠消息最终一致性05:保存并发送事务消息
在有了分布式事务的解决方案、项目的需求、骨架代码和基础代码,做好了所有的准备工作之后,接下来就可以继续深入了解「核心业务」了。 在前面了解分布式事务时,可靠消息最终一致性方案的流程图是这样的: 图三十一:可靠消息最终一致性 整个的流程是: 1、业务处理服务在事务…...
SQL语句大全(详解)
SQL前言1 DDL1.1 显示所包含的数据库1.2 创建数据库1.3 删除数据库1.4 使用数据库1.4.1 创建表1.4.2 查看表的结构1.4.3 查看当前数据库下的所有表1.4.4 基础的增删改查1.4.4.1 删除表1.4.4.2 添加列1.4.4.3 修改表名1.4.4.4 修改数据类型1.4.4.5 修改列名和数据类型2 DML2.1 给…...
视频营销活动中7个常见的错误
如今,越来越多的企业在社交媒体平台上开展视频营销活动。与其他传统营销策略不同,视频营销可以为企业带来更多的销售机会。随着越来越多的视频社交媒体平台的出现,营销人员更应该抓住这个机会。但在开始视频创作之前,您需要有一个…...
MapReduce小试牛刀
部署完hadoop单机版后,试下mapreduce是怎么分析处理数据的 Word Count Word Count 就是"词语统计",这是 MapReduce 工作程序中最经典的一种。它的主要任务是对一个文本文件中的词语作归纳统计,统计出每个出现过的词语一共出现的次…...
2023年全国最新工会考试精选真题及答案7
百分百题库提供工会考试试题、工会考试预测题、工会考试真题、工会证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 21.会员大会或会员代表大会与职工代表大会或职工大会须分别行使职权,()…...
13-mvc框架原理与实现方式
1、mvc原理 # mvc 与框架## 1.mvc 是什么1. m:model,模型(即数据来源),主要是针对数据库操作 2. v:view,视图,html 页面。视图由一个一个模板构成(模板是视图的一个具体展现或载体,视图是模板的一个抽象) 3. c:controller,控制器,用于mv之间的数据交互## 2.最简单的 mvc 就是一…...
弹性盒子布局
目录一、弹性盒子属性二、认识flex的坐标轴三、简单学习父级盒子属性三、属性说明3.1、flex-grow一、弹性盒子属性 说明: div的默认样式:display:block 块盒子 display:flex弹性盒子(可以控制下级盒子的位置) 当两种盒子单独出现…...
C# Sqlite数据库加密
sqlite官方的数据库加密是收费的,而且比较贵。 幸亏微软提供了一种免费的方法。 1 sqlite加密demo 这里我做了一个小的demo演示如下: 在界面中拖入数据库名、密码、以及保存的路径 比如我选择保存路径桌面的sqlite目录,数据库名guigutool…...
高压放大器在声波谐振电小天线收发测试系统中的应用
实验名称:高压放大器在声波谐振电小天线收发测试系统中的应用研究方向:信号传输测试目的:声波谐振电小天线颠覆了传统电小天线以电磁波谐振作为理论基础的天线发射和接收模式,它借助声波谐振实现电磁信号的辐射或接收。因为同频的…...
锁相环的组成和原理及应用
一.锁相环的基本组成 许多电子设备要正常工作,通常需要外部的输入信号与内部的振荡信号同步,利用锁相环路就可以实现这个目的。 锁相环路是一种反馈控制电路,简称锁相环(PLL)。锁相环的特点是:利用外部输入的参考信号控制环路内…...
[C++]string类模拟实现
目录 前言: 1. string框架构造 2. 默认函数 2.1 构造函数 2.2 析构函数 2.3 拷贝构造 2.4 赋值重载 3. 迭代器 4. 整体程序 前言: 本篇文章模拟实现了C中string的部分功能,有助于大家了解和熟悉string类,虽然这个类不难实…...
一个更适合Java初学者的轻量级开发工具:BlueJ
Java是世界上最流行的编程语言之一,它被广泛用于从Web开发到移动应用的各种应用程序。大部分Java工程师主要是用IDEA、Eclipse为主,这两个开发工具由于有强大的能力,所以复杂度上就更高一些。如果您刚刚开始使用Java,或者您更适合…...
从程序员到项目组长,要经历六重修炼
最近和粉丝朋友们交流时发现,有很多刚刚开始做项目组长的朋友自我认可度非常低,感觉做组长之后天天打杂,技术也荒废了。领导天天找你要成果,下属天天找你说困难,你在中间受领导和下属的夹板气。时间久了,你…...
我的 System Verilog 学习记录(5)
、 引言 本文简单介绍 System Verilog 语言的 控制流。 前文链接: 我的 System Verilog 学习记录(1) 我的 System Verilog 学习记录(2) 我的 System Verilog 学习记录(3) 我的 System Ver…...
多芯片设计 Designing For Multiple Die
Why a system-level approach is essential, and why its so challenging作者:Ann MutschlerAnn Mutschler is executive editor at Semiconductor Engineering.将多个裸片或芯粒集成到一个封装中,与将它们放在同一硅片上有着很大的区别。在同一硅片上&a…...
2022年全国职业院校技能大赛(中职组)网络安全竞赛试题A(10)
目录 竞赛内容 模块A 基础设施设置与安全加固 一、项目和任务描述: 二、服务器环境说明 三、具体任务(每个任务得分以电子答题卡为准) A-1任务一 登录安全加固(Windows, Linux) 1.密码策略(Windows, …...
数据结构-简介
目录 1、简介 2、作用 3、分类 4、实现分类 1、简介 数据结构指的是组织和存储数据的方法。它涉及到一系列的算法和原则,用来设计和实现不同种类的数据类型,如数组、链表、树、图等等。数据结构的目的是在计算机程序中有效地管理和操作数据ÿ…...
python装饰器及其用法
python装饰器是什么? Python装饰器是一种语法结构,它可以让开发者在不修改原函数的基础上,在函数的前后运行额外的代码,这些代码可以达到修改函数行为的目的。Python装饰器的实质是一个可调用的对象,它可以接收函数作为参数…...
Appium自动化测试之启动时跳过初始化设置
Appium每次启动时都会检查和安装Appium Settings,这是完全没有必要的,在首次使用Appium连接设备是Appium Settings便已经安装好。怎样跳过安装Appium Settings呢?之前的做法是修改appium中的源文件中的android-helpers.js实现,如M…...
JavaScript DOM【快速掌握知识点】
目录 DOM简介 获取元素 修改元素 添加和移除元素 事件处理 DOM简介 JavaScript DOM 是指 JavaScript 中的文档对象模型(Document Object Model);它允许 JavaScript 与 HTML 页面交互,使开发者可以通过编程方式动态地修改网页…...
不需要高深技术,只需要Python:创建一个可定制的HTTP服务器!
目录 1、编写服务端代码,命名为httpserver.py文件。 2、编写网页htmlcss文件,命名为index.html和style.css文件。 3、复制htmlcss到服务端py文件同一文件夹下。 4、运行服务端程序。 5、浏览器中输入localhost:8080显示如下: 要编写一个简单的能发布…...
渗透测试常用浏览器插件汇总
1、shodan这个插件可以自动探测当前网站所属的国家、城市,解析IP地址以及开放的服务和端口,包括但不限于FTP、DNS、SSH或者其他服务等,属被动信息搜集中的一种。2、hackbar(收费之后用Max Hackerbar代替)这个插件可用于…...
社区1月月报|OceanBase 4.1 即将发版,哪些功能将会更新?
我们每个月都会和大家展开一次社区进展的汇报沟通会,希望通过更多的互动交流让OceanBase 开源社区更加透明,实现信息共享,也希望能营造更加轻松的氛围,为大家答疑解惑,让大家畅所欲言。如果您对我们的社区有任何建议&a…...
Javascript的API基本内容(二)
一、事件监听 结合 DOM 使用事件时,需要为 DOM 对象添加事件监听,等待事件发生(触发)时,便立即调用一个函数。 addEventListener 是 DOM 对象专门用来添加事件监听的方法,它的两个参数分别为【事件类型】和…...
网址大全黄免费片/江苏网站seo设计
为选型卡的最外层容器添加swiper-no-swiping类名 <!-- swiper-no-swiping 类名,禁止选项卡的滑动事件--> <div class"swiper-pagination swiper-no-swiping"></div>...
论坛网站开发的意义/网站免费网站免费
本博客是开发blackberry10的记录,不知道黑莓能走多远,也不知道我喜爱黑莓的心能走多远 希望此地是黑莓10开发的乐园,也希望能陪伴黑莓一直走下去,更希望能给黑莓开发的朋友一个交流的平台转载于:https://www.cnblogs.com/bb10/arc…...
wordpress 登陆隐藏/新闻摘抄
人工智能不断地突破着我们的想象力,AI系统也在快速地进入现实世界,这种情况下,人与机器该如何相处?微软研究院资深研究员Ece Kamar就人机互补表达了她的看法,并揭示了人们对AI的一些常见误解。她相信,机器善…...
wordpress twenty six/百度sem代运营
spinlock设计成了三层,第一层是接口,第二层raw实现层,第三层是CPU平台层。在第二层raw实现层提供了两个分支,分别是单CPU和多CPU(核)。第三层是不同CPU的锁操作实现。raw层除了调用锁操作来保护临界资源外&…...
做旅游网站会遇到什么问题/友情连接
我本来给图片设置了object-fit:cover; 但是 html2canvas画完之后 竟然消失了 这个就很奇怪了,包括图片的圆角也没有了, 我翻了翻一些文档说 子元素设置相对于父元素宽高 然后在设置object-fit (我试了试不行pass掉了)…...
有什么网站可以免费建站免费建网站/百度百科查询
前言 weblogic反序列化主要有XMLDecoder和T3协议。先从T3协议开始,主要是CVE-2015-4852这个漏洞 环境搭建 https://blog.csdn.net/qq_41918771/article/details/117467957https://blog.csdn.net/weixin_45682070/article/details/123230456主要参考这两篇文章,第一篇是我写…...