简单易懂值得收藏的Spring源码解析,依赖注入和bean的初始化

news2024/9/19 22:21:26

简单易懂值得收藏的Spring源码解析,依赖注入和bean的初始化

  • 原理解析
    • 依赖注入
      • PropertyValues、PropertyValue、PropertyAccessor
      • byName
      • byType
      • @Autowired
    • bean的初始化
  • 源码走读
    • 依赖注入
      • populateBean方法
      • autowireByName
      • autowireByType方法
      • AutowiredAnnotationBeanPostProcessor#postProcessProperties
    • bean的初始化
  • 总结

往期文章:

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

上一篇文章讲到了bean的实例化过程。bean的实例化完成之后,就要对bean进行属性赋值,然后进行初始化。

一个bean从配置到初始化完成放入容器中,要经历四个阶段:

  1. 从bean配置到BeanDefinition
  2. bean实例化
  3. 依赖注入
  4. 初始化

在这里插入图片描述

前两个阶段已经在前两篇文章介绍完,因此本篇文章将重点介绍后面两个阶段,bean的依赖注入初始化这两大过程。

原理解析

下面对依赖注入和bean的初始化这两个过程的原理进行解析。

依赖注入

首先是依赖注入的原理解析。

PropertyValues、PropertyValue、PropertyAccessor

首先要理解与依赖注入相关的三个重要的类PropertyValuesPropertyValuePropertyAccessor

下面是这三者的关系图:

在这里插入图片描述

Spring再bean的依赖注入的时候,不是处理到一个属性就给一个属性赋值的,而是会把一个bean的属性,以及该属性依赖的对象封装到一个PropertyValue对象中,然后把PropertyValue放入到PropertyValues中,PropertyValues相当于是存放PropertyValue的容器,里面其实是一个List

收集好了一个bean的所有的PropertyValue到PropertyValues后,将交给PropertyAccessor属性访问器处理。PropertyAccessor通过 method.invoke(…) 或者 field.set(…) 等反射的方法进行属性赋值。

在这里插入图片描述

PropertyAccessor是一个接口,然后BeanWrapper接口又继承了该接口,所以最终实现类就是BeanWrapperImpl

在这里插入图片描述

那Spring是如何解析bean的属性依赖关系,生成PropertyValue并放入PropertyValues中的呢?下面将进行介绍。

byName

首先是byName模式的依赖注入,就是通过属性名propertyName作为beanName,调用**getBean(propertyName)**方法获取依赖对象,然后和propertyName一起封装成PropertyValue对象,放入PropertyValues中。

在这里插入图片描述

byType

byType模式的依赖注入比byName稍微复杂:

  1. 先通过类型从容器中获取与该类型匹配的所有beanName,返回一个beanName数组candidateNames
  2. 遍历candidateNames,获取对应的Class对象放入到一个Map中
  3. 遍历结束后,该Map如果size大于1,则要从该Map中推断出一个最合适的beanName。推断逻辑就是@Primary注解修饰的优先考虑,如果没有就看有没有@Priority注解修饰的,也没有就寻找beanName和属性名匹配的。然后通过beanName调用getBean获取依赖的对象。
  4. 最后和属性名一起封装成PropertyValue对象放入PropertyValues中

在这里插入图片描述

但是在上面这个逻辑之前,Spring还会回调AutowireCandidateResolver接口实现类的getSuggestedValue方法,该接口是一个扩展点,允许我们对依赖注入上做自定义处理,如果该方法返回值不为空,就会注入我们给定的值,不会往下走上面的逻辑。

在这里插入图片描述

所以byType的整体逻辑就如下图:

在这里插入图片描述

@Autowired

除了byType和byName两种模式,还有就是通过**@Autowired**注解进行依赖注入。@Autowired注解修饰的属性的依赖注入又 AutowiredAnnotationBeanPostProcessor 这个bean后置处理器进行处理,处理逻辑与byType一致

在这里插入图片描述

bean的初始化

接下来是bean的初始化的原理解析。

一共四个步骤:

  1. Aware接口的回调
  2. bean后置处理器before方法的回调
  3. bean的初始化方法的回调
  4. bean后置处理器after方法的回调

在这里插入图片描述

源码走读

接下来进行源码走读,对上面的分析进行验证。

依赖注入

populateBean方法

依赖注入处理逻辑的入口,位于AbstractAutowireCapableBeanFactory的doCreateBean方法里面的populateBean(beanName, mbd, instanceWrapper) 这一行代码。

在这里插入图片描述

populateBean方法里面可以看到byName和byType两种模式的依赖注入的入口,autowireByName方法处理byName模式的依赖注入,autowireByType方法处理byType模式的依赖注入。

在这里插入图片描述

下面还有对@Autowired注解修饰的属性的依赖注入的处理入口,实现了InstantiationAwareBeanPostProcessor接口的后置处理器,就调用postProcessProperties方法。

在这里插入图片描述

populateBean的整体逻辑如下图:

在这里插入图片描述

autowireByName

进入autowireByName方法,看一下byName模式依赖注入的处理逻辑。

在这里插入图片描述

**unsatisfiedNonSimpleProperties(mbd, bw)获取当前bean所有待注入的属性 String[] propertyNames,然后遍历propertyNames,通过getBean(propertyName)以propertyName作为beanName从容器中获取,然后调用pvs.add(propertyName, bean)**添加到PropertyValues中。

进入pvs.add(propertyName, bean)。

在这里插入图片描述

在这里插入图片描述

可以看到就是把属性名和依赖的对象封装成一个PropertyValue对象,放到PropertyValues里面的一个List中。

在这里插入图片描述

autowireByType方法

然后在进去autowireByType方法,看看byType模式的依赖注入的处理逻辑。

在这里插入图片描述
可以看到跟byName的区别就是获取依赖对象不再是直接通过getBean方法,而是调用了resolveDependency方法。

在这里插入图片描述
resolveDependency方法又调用了doResolveDependency方法。

在这里插入图片描述

doResolveDependency方法里面首先调用AutowireCandidateResolver的getSuggestedValue方法尝试获取给定的依赖对象或值value,如果返回的value不为空,就以该value作为要注入当当前属性的值。

在这里插入图片描述

然后Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor)这一行代码就是通过beanName获取Class对象,然后返回一个Map,key是beanName,value是对应的Class对象。

在这里插入图片描述

这一行代码就是判断如果Map的size大于1,则推断出一个合适的beanName。

在这里插入图片描述
在这里插入图片描述
最后取得最合适的beanName后,还是getBean(beanName)获取依赖对象。

在这里插入图片描述

AutowiredAnnotationBeanPostProcessor#postProcessProperties

接下来看一下对@Autowired注解修饰的属性的依赖注入,这里会回调所有InstantiationAwareBeanPostProcessor的postProcessProperties方法,AutowiredAnnotationBeanPostProcessor实现了InstantiationAwareBeanPostProcessor接口,因此会进入AutowiredAnnotationBeanPostProcessor的postProcessProperties方法

在这里插入图片描述

进入AutowiredAnnotationBeanPostProcessor的postProcessProperties方法,从metadata.inject(bean, beanName, pvs)这一行代码进去。

在这里插入图片描述

继续,从element.inject(target, beanName, pvs)这一行代码进行。

在这里插入图片描述

如果@Autowired修饰在字段上,会进入到AutowiredFieldElement#inject方法;如果是@Autowired注解修饰在set方法上,会进入到AutowiredMethodElement#inject方法。

先来看AutowiredFieldElement#inject方法。
在这里插入图片描述
在AutowiredFieldElement#inject方法里面,可以看到又是调用beanFactory的resolveDependency方法,所以跟byType的逻辑一样。

如果返回结果不为空,就通过field.set(bean, value)反射注入。

在这里插入图片描述
再来看下AutowiredMethodElement#inject方法。

在这里插入图片描述

还是调用了beanFactory的resolveDependency方法,与byType的逻辑一样。

如果返回结果不为空,则调用method.invoke(bean, arguments)进行反射注入。

在这里插入图片描述

在这里插入图片描述

依赖注入的代码走读就到这里,下面就是bean的初始化。

bean的初始化

bean的初始化的入口位于AbstractAutowireCapableBeanFactory#doCreateBean方法里面的 exposedObject = initializeBean(beanName, exposedObject, mbd) 这一行代码。

在这里插入图片描述

进入initializeBean(beanName, exposedObject, mbd)方法。

在这里插入图片描述

可以看到就是原来分析里面的四步,非常清晰。**invokeAwareMethods(beanName, bean)**就是Aware接口的回调,**applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)**就是bean后置处理器before方法的回调,invokeInitMethods(beanName, wrappedBean, mbd) 就是bean的初始化方法的回调,applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName) 就是bean后置处理器after方法的回调。

在这里插入图片描述

总结

以上就是依赖注入和bean的初始化的全部内容,下面做一个简单总结。

依赖注入:

  • byName模式会通过属性名调用getBean方法获取依赖对象,然后封装为PropertyValue放入到PropertyValues中,后面统一进行注入处理。
  • byType会先回调AutowiredCandidateResolver的getSuggestedValue方法,如果返回为null,则根据类型获取与该类型匹配的所有beanName,然后再弄出一个key为beanName,value为Class对象的Map,如果该Map的size大于1,则要推断出一个最合适的beanName,然后通过getBean获取依赖对象,然后封装为PropertyValue放入到PropertyValues中,后面统一进行注入处理。
  • @Autowired注解修饰的字段的依赖注入,则通过bean后置处理器AutowiredAnnotationBeanPostProcessor进行处理,里面的逻辑与byType一致。

bean的初始化:

  1. Aware接口的回调
  2. bean后置处理器的before方法回调
  3. bean的初始化方法的回调
  4. bean后置处理器的after方法回调

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

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

相关文章

广义状态平均法功率变换器建模分析

两种状态平均法在功率变换器建模的应用比较 [!info] Bibliography [1] 高朝晖, 林辉张晓斌 & 吴小华, “两种状态平均法在功率变换器建模的应用比较,” 计算机仿真, no. 241-244248, 2008. [!note] 状态空间平均法采用直流量近似&#xff08;线性系统模型&#xff09;&…

通达信指标公式颜色代码的四种写法(COLOR/RGB)

通达信指标公式颜色代码有四种写法&#xff0c;分别为COLOR颜色的英文、COLOR十六进制、RGBX十六进制、RGB(R,G,B)。标题有点尴尬&#xff0c;让我想到孔乙己“茴”字的四种写法&#xff0c;哈哈。 一、COLOR颜色的英文 “COLOR颜色的英文”这种写法比较简单&#xff0c;函数库…

【C++】bsearch函数的使用及二分法查找介绍

写程序的时候&#xff0c;肯定避免不了需要从集合中找到符合条件的元素&#xff0c;一般情况下&#xff0c;最简单也最常用的就是循环遍历元素&#xff0c;这种方法虽然写的简单&#xff0c;但是小数据量还行&#xff0c;但是数据过大的话&#xff0c;这样效率就低了。循环的时…

浅谈游戏中运用到的人工智能

电子游戏中的人工智能意味着电子游戏中角色的创造性行为就像人类游戏玩家正在控制他们一样。它提供了自适应和响应式的视频游戏体验。1949年初&#xff0c;密码学家克劳德香农发现了游戏中的人工智能&#xff0c;从而联想到了电脑上的单人国际象棋游戏。对于人工智能的发展&…

Android的基础介绍

一、Android介绍 Android是一种基于Linux的自由及开放源代码的操作系统,Android 分为四个层,从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和Linux内核层。 Android 是Google开发的基于Linux平台的开源手机操作系统。它包括操作系统、用户界面和应用程序——…

力扣-可回收且低脂的产品

大家好&#xff0c;我是空空star&#xff0c;本篇带大家了解一道超级超级超级简单的力扣sql练习题。 文章目录前言一、题目&#xff1a;1757. 可回收且低脂的产品二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.正确示范④提交S…

iview tree树形菜单实践之数据回显与选中

iview tree树形菜单在使用过程中&#xff0c;多多少少有一些小坑&#xff0c;本文简单罗列几个&#xff1a; 避坑指南&#xff1a; 关于iview tree树形菜单在使用过程中存在父级菜单的数据传输和回显问题 简单就是两个方面&#xff1a;勾选后数据传输和回显问题。 一&#xff…

mac 环境下安装MongoDB

目录 一、下载MongoDB数据库并进行安装 二. 解压放在/usr/local目录下 三. 配置环境变量 “无法验证开发者”的解决方法 mongodb可视化工具的安装与使用 一、下载MongoDB数据库并进行安装 下载地址&#xff1a;https://www.mongodb.com/try/download/community 二. 解压…

大数据项目实战之数据仓库:用户行为采集平台——第2章 项目需求及架构设计

第2章 项目需求及架构设计 2.1 项目需求分析 1&#xff09;采集平台 &#xff08;1&#xff09;用户行为数据采集平台搭建 &#xff08;2&#xff09;业务数据采集平台搭建 2&#xff09;离线需求 3&#xff09;实时需求 4&#xff09;思考题 1、项目技术如何选型&…

索引调优的一点基础知识

索引是什么 通过 show profile for query 1&#xff1b; 可以查看该查询语句执行的整个过程&#xff0c;则我们就能分析出那部分是需要优化的索引是帮助Mysql高效获取数据的数据结构 索引存储在文件系统中 索引的文件存储形式与存储引擎有关 索引文件的结构 hash 范围查找不方便…

深度学习-第T2周——彩色图片分类

深度学习-第T2周——彩色图片分类深度学习-第P1周——实现mnist手写数字识别一、前言二、我的环境三、前期工作1、导入依赖项并设置GPU2、导入数据集3、归一化4、可视化图片四、构建简单的CNN网络五、编译并训练模型1、设置超参数2、编写训练函数六、预测七、模型评估深度学习-…

产品经理如何去做需求分析,建议收藏

目录 一、需求收集 1. 产品需求 2. 功能需求 3. 迭代需求 二、需求分析 1. 系统了解需求&#xff08;5W2H法&#xff09; 2. 判断需求真实性 3. 分析现有解决方案-竞品分析 4. 行业结构分析 5. 总结 三、方案验证 四、商业模式 五、路线规划 六、战略制定 七、总…

基于贝叶斯优化CNN-LSTM混合神经网络预测(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密…

监控视频模糊看不清怎么处理提高监控视频清晰度?

随着监控设备的日益普及&#xff0c;监控视频的画质也成为了人们关注的焦点。然而&#xff0c;很多人反映监控视频画质不佳&#xff0c;有些甚至看不清。那么&#xff0c;对于这样的问题&#xff0c;我们该如何去处理呢&#xff1f;监控画面模糊变清晰处理方法如下&#xff1a;…

网上购物网站的设计

技术&#xff1a;Java、JSP等摘要&#xff1a;本文介绍了JSP和JAVA等相关技术&#xff0c;针对网上购物系统的实际需求&#xff0c;设计开发了一个基于JSP的小型电子商务网站也就是网上购物系统&#xff0c;。在设计开发中&#xff0c;采用的是SSH框架&#xff08;strutsspring…

一篇教你解决如何在不加锁的情况下解决多线程问题!

怎样在不加锁的情况下解决线程安全问题&#xff0c;你需要了解lock free和wait free这两个概念&#xff0c;在此之前我们先从最简单的有锁编程开始。 我们知道&#xff0c;多线程同时修改共享变量时会出现数据不一致的问题&#xff0c;比如多个线程同时对一个变量加1&#xff…

angular自定义实现管道

参考angular官方文档角 - 管 (angular.io)选择描述name要在模板绑定中使用的管道名称。 通常使用较小的驼峰大小写&#xff0c;因为名称不能包含连字符。pure?如果为 true&#xff0c;则管道是纯的&#xff0c;这意味着仅当该方法的输入参数时才调用该方法 改变。默认情况下&a…

JavaScript基础四、集合类型

零、文章目录 文章地址 个人博客-CSDN地址&#xff1a;https://blog.csdn.net/liyou123456789个人博客-GiteePages&#xff1a;https://bluecusliyou.gitee.io/techlearn 代码仓库地址 Gitee&#xff1a;https://gitee.com/bluecusliyou/TechLearnGithub&#xff1a;https:…

C++源程序的构成————学习笔记

以下内容为&#xff0c;在学校上课时的课堂总结&#xff0c;偶尔我也会扩展一些内容内容仅供参考&#xff0c;欢迎大佬的指正简单的C程序#include <iostream> using namespace std;int main() {int x0;int y 0;cout << "请输入x,y的值"<<endl;cin…

计算机网络第八版——第二章课后题答案(超详细)

第二章 该答案为博主在网络上整理&#xff0c;排版不易&#xff0c;希望大家多多点赞支持。后续将会持续更新&#xff08;可以给博主点个关注~ 第一章 答案 【2-01】物理层要解决哪些问题&#xff1f;物理层的主要特点是什么&#xff1f; 解答&#xff1a;物理层考虑的是怎…