简单易懂又非常牛逼的Spring源码解析,推断构造与bean的实例化

news2024/9/30 11:31:22

简单易懂又非常牛逼的Spring源码解析,推断构造与bean的实例化

  • 原理解析
    • 实例化bean的入口
    • 工厂方法实例化
    • 推断构造
      • 初次筛选
      • 二次筛选
    • bean的实例化
  • 代码走读
    • 实例化bean的入口
    • createBeanInstance方法内部的流程
    • 推断构造
      • 初次筛选
      • 二次筛选
    • bean的实例化
  • 总结

往期文章:

  1. 人人都能看懂的Spring底层原理,看完绝对不会懵逼
  2. 简单易懂的Spring扩展点详细解析,看不懂你来打我
  3. 人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册
  4. 简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑

上一篇文章讲到了ConfigurationClassPostProcessor的具体逻辑,至此所有的配置信息已经被解析成BeanDefinition,注册到容器中了。

下一步就是根据BeanDefinition进行非懒加载的单例bean的实例化。因此,本篇文件就要介绍非懒加载单例bean的实例化过程。

原理解析

实例化bean的入口

要研究bean的实例化,首先就要找到bean的实例化的入口。

在这里插入图片描述

  1. Spring首先会进行配置文件或者配置类的解析,然后生成BeanDefinition并注册到容器中
  2. 待所有的BeanDefinition都注册完毕后,会遍历每一个BeanDefinition,判断是否是非抽象、单例、非懒加载,如果是,则调用getBean(beanName)方法进行bean的预加载
  3. 然后会尝试从容器中获取,看是否已经初始化完成,如果没有,则进行bean的实例化
  4. 而bean的实例化的入口,就是createBeanInstance方法

这个createBeanInstance方法,就是我们这篇文件研究的主体。

工厂方法实例化

bean的实例化有两种方式,一种是工厂方法实例化,一种是构造器实例化。

createBeanInstance方法,首先会检查BeanDefinition中的factoryMethodName属性是否不为空,如果不为空,就会使用工厂方法进行实例化。

我们上一篇文章分析的@Bean注解修饰的方法,生成的BeanDefinition就带有factoryMethodName属性。

在这里插入图片描述

如果BeanDefinition中的factoryMethodName属性为空,就要通过构造器进行实例化。

推断构造

构造器实例化,先要进行构造器的挑选。

如果要通过构造器进行实例化的话,就要先挑选一个合适的构造器,因为一个类里面构造器可能有多个。

在这里插入图片描述

推断构造:

  1. 第一步初次筛选,返回一个构造器数组
  2. 初次筛选返回的构造器数组不为空,或者自动注入模型为构造器注入,或者我们指定了构造器参数,则进行二次筛选,挑出一个最合适的
  3. 否则走最简单的逻辑,使用无参构造器进行实例化

在这里插入图片描述

初次筛选

初次筛选的条件如下,符合条件的构造器,会放到一个数组里面返回。

在这里插入图片描述

二次筛选

二次筛选就是从初次筛选的结果中,再筛出一个最合适的。

这里主要就是筛选出要使用的构造器,以及构造器要使用的参数,筛选出的构造器会保存到constructorToUse变量,而构造器要使用的参数则保存到argsToUse变量。

在这里插入图片描述

上面这张图就是推断构造二次筛选的流程,比较复杂,这里看不懂的可以忽略,非重点

  1. 首先会判断我们是否手动传入了参数,如果是,则使用我们手动传的参数
  2. 如果我们没有手动传参,则看BeanDefinition中是否缓存了曾经筛选出的构造器和参数,如果是,则使用缓存的
  3. 如果BeanDefinition没有缓存,看是否只有一个无参构造器,如果是,则使用该构造器,并缓存到BeanDefinition中
  4. 如果有多个构造器,先确定最少需要的参数个数minNrOfArgs(取值情况看上图)
  5. 然后对构造器进行排序(排序条件看上图)
  6. 遍历构造器数组
  7. 如果已经挑选出最合适的构造器,当前遍历到的构造器需要的参数又更少,则break跳出循环
  8. 如果当前遍历到的构造器需要的参数个数,少于minNrOfArgs,continue跳过该构造器
  9. 没有break,也没有continue,那么就要获取当前构造器需要的参数(如何获取看上图)
  10. 然后计算取得的参数个数和 构造器所需参数间的差值
  11. 跟之前选出的构造器进行比较,保留差值更小的构造器
  12. 遍历结束
  13. 如果不是主动传参的情况,则把筛选结果缓存到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的实例化源码解析的所有内容,下面做一个总结。

  1. 推断构造首先会经过第一次筛选,筛出一个构造器数组
  2. 然后构造器数组不为空,或者当前BeanDefinition指定为构造器注入,或者我们在配置中指定了构造器参数,或者我们getBean的时候传递了参数,那么就会经历第二次筛选,选出一个最合适的构造器以及构造器参数
  3. 否则就会使用无参构造器进行实例化
  4. 然后会通过InstantiationStrategy(实例化策略)进行实例化,里面调用BeanUtils工具类,最终通过构造器进行反射实例化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/375021.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

十六、vue3.0之富文本编辑器的选择

在工作过程中我们会遇到很多的时候会使用到富文本编辑器,市场上流行的也是各种各样的,那么究竟如何选择呢,今天就给大家讲讲有哪一些,方便大家的选择。 一、TinyMCE TinyMCE 是富文本编辑器领域的头部玩家之一,主流富文本编辑器,功能非常全,你需要的大多数功能它都支持…

深力科电子-MachXO3系列 LCMXO3LF-1300C-5BG256C 控制和桥接FPGA器件

深力科电子-lattice莱迪斯MachXO3系列 LCMXO3LF-1300C-5BG256C 控制和桥接FPGA器件 ,FPGA 现场可编程逻辑器件,小尺寸,高性能!在工业领域,它可以用于网络控制器,PLC,网络边缘计算,机器视觉和工业机器人&…

万字解析 Linux 中 CPU 利用率是如何算出来的?

在线上服务器观察线上服务运行状态的时候,绝大多数人都是喜欢先用 top 命令看看当前系统的整体 cpu 利用率。例如,随手拿来的一台机器,top 命令显示的利用率信息如下 这个输出结果说简单也简单,说复杂也不是那么容易就能全部搞明白…

【java web篇】使用JDBC操作数据库

📋 个人简介 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜📝 个人主页:馆主阿牛🔥🎉 支持我:点赞👍收藏⭐️留言&#x1f4d…

学习Java前,应该了解的这些知识(新手必学)

Java语言广泛应用于编写web应用程序、移动开发、安卓开发等,市场上对Java人才需求量很大,有数据显示,Java工程师的薪资待遇随着人才市场的需求逐步递增,由此可见,Java人才需求量呈现持续上升趋势,供不应求。…

LevelDB架构介绍以及读、写和压缩流程

LevelDB 基本介绍 是一个key/value存储,key值根据用户指定的comparator排序。 特性 keys 和 values 是任意的字节数组。数据按 key 值排序存储。调用者可以提供一个自定义的比较函数来重写排序顺序。提供基本的 Put(key,value),Get(key),…

企业电子招标采购源码之电子招标投标全流程!

随着各级政府部门的大力推进,以及国内互联网的建设,电子招投标已经逐渐成为国内主流的招标投标方式,但是依然有很多人对电子招投标的流程不够了解,在具体操作上存在困难。虽然各个交易平台的招标投标在线操作会略有不同&#xff0…

13-mvc框架原理与实现方式

1、mvc原理 # mvc 与框架## 1.mvc 是什么1. m:model,模型(即数据来源),主要是针对数据库操作 2. v:view,视图,html 页面。视图由一个一个模板构成(模板是视图的一个具体展现或载体,视图是模板的一个抽象) 3. c:controller,控制器,用于mv之间的数据交互## 2.最简单的 mvc 就是一…

Java+Swing+Mysql实现超市管理系统

一、系统介绍1.开发环境操作系统:Win10开发工具 :IDEA2018JDK版本:jdk1.8数据库:Mysql8.02.技术选型JavaSwingMysql3.功能模块4.系统功能1.系统登录登出管理员可以登录、退出系统2.商品信息管理管理员可以对商品信息进行查询、添加…

MapReduce小试牛刀

部署完hadoop单机版后,试下mapreduce是怎么分析处理数据的 Word Count Word Count 就是"词语统计",这是 MapReduce 工作程序中最经典的一种。它的主要任务是对一个文本文件中的词语作归纳统计,统计出每个出现过的词语一共出现的次…

【云原生kubernetes】k8s 常用调度策略使用详解

一、前言 通过之前的学习,我们了解到k8s集群中最小工作单位是pod,对于k8s集群来说,一个pod的完整生命周期是由一系列调度策略来控制,这些调度策略具体是怎么工作的呢?本文将详细讨论下这个问题。 二、k8s调度策略简介…

K8S---Pod进阶资源限制以及探针

目录 一、Pod 进阶 1、资源限制 2、Pod 和 容器 的资源请求和限制: 3、CPU 资源单位 4、内存 资源单位 5、实例操作 5.1 示例1 5.2 示例2 6、重启策略(restartPolicy) 6.1 示例1 二、健康检查:又称为探针(P…

华为OD机试题,用 Java 解【机器人走迷宫】问题

最近更新的博客 华为OD机试题,用 Java 解【停车场车辆统计】问题华为OD机试题,用 Java 解【字符串变换最小字符串】问题华为OD机试题,用 Java 解【计算最大乘积】问题华为OD机试题,用 Java 解【DNA 序列】问题华为OD机试 - 组成最大数(Java) | 机试题算法思路 【2023】使…

电动针阀流量控制和电气比例阀压力控制在液氮低温控制中的应用

摘要:为了解决室温至液氮温区温控系统中需要昂贵的低温电动阀门进行液氮介质流量调节的问题,本文提供了三种不同精度的液氮温区内的低温温度控制解决方案。解决方案的技术核心是通过采用电动针阀和电气比例阀在室温环境下来快速调节外部气源流量或压力大…

Nginx网站服务——编译安装、基于授权和客户端访问控制

文章目录一、Nginx概述1.1、Nginx的特点1.2、Nginx编译安装1.3、Nginx运行控制1.4、Nginx和Apache的区别二、编译安装Nginx服务的操作步骤2.1、关闭防火墙,将安装nginx所需软件包传到/opt目录下2.2、安装依赖包2.3、创建运行用户、组(Nginx 服务程序默认…

【2023全网最全教程】从0到1开发自动化测试框架(建议收藏)

一、序言 随着项目版本的快速迭代、APP测试有以下几个特点: 首先,功能点多且细,测试工作量大,容易遗漏;其次,代码模块常改动,回归测试很频繁,测试重复低效;最后&#x…

Java 多线程 --- 多线程的相关概念

Java 多线程 --- 多线程的相关概念Race Condition 问题并发编程的性质 --- 原子性, 可见性, 有序性上下文切换 (Context Switch)线程的一些故障 --- 死锁, 活锁, 饥饿死锁 (Deadlock)活锁(Livelock)死锁和活锁的区别饥饿(Starvation)背景: 操作系统 — 线程/进程 同步 Race Co…

Windows操作系统的体系结构、运行环境和运行状态

我是荔园微风,作为一名在IT界整整25年的老兵,今天我们来重新审视一下Windows这个我们熟悉的不能再熟悉的系统。说Windows操作系统的运行环境和运行状态,首先要介绍一下Windows操作系统的体系结构,然后再要说到最重要的两个概念:核…

【架构师】零基础到精通——微服务体系

博客昵称:架构师Cool 最喜欢的座右铭:一以贯之的努力,不得懈怠的人生。 作者简介:一名Coder,软件设计师/鸿蒙高级工程师认证,在备战高级架构师/系统分析师,欢迎关注小弟! 博主小留言…

Iterator和Genertator

一、Iterator迭代器和for of原理 * 遍历器(Iterator)是一种机制(接口):为各种不同的数据结构提供统一的访问机制,任何数据结构只要部署Iterator接口,就可以完成遍历操作「for of循环」,依次处理该数据结构的…