Sprng依赖注入(三):构造方法注入是如何工作的?
前言
这是Spring依赖注入系列的第三篇,前两篇主要分析了Spring bean依赖属性注入的两种方式,是字段注入和setter方法注入,单独比较这两种方式,会发现其过程和工作原理非常类似,那么构造方法注入会不会也和前两种比较类似呢?本篇文章将会揭晓答案。
构造方法注入方法
以@Autowired注解为例,即把@Autowired注解标记在目标bean的构造方法上,而构造方法的入参数是引用bean类型;
构造方法注入示例
示例主要内容:1、定义Teachert类; 2、定义Student类;3、在Student类中依赖Teacher;4、使用@Autowired注解标记在Student(Teacher teacher)上,即在Student对象中以构造方法注入的方式注入Teacher对象;
@Slf4j
@Component
public class Student {private String name="小明";private Teacher teacher;public Student() {log.info("----student的无参数构造方法被执行");}@Autowiredpublic Student(Teacher teacher) {this.teacher = teacher;log.info("----student的有参数构造方法(teacher)被执行");}public Student(String name, Teacher teacher) {this.name = name;this.teacher = teacher;log.info("----student的有参数构造方法(name,teacher)被执行");}public String getName() {return name;}public void setName(String name) {this.name = name;}public Teacher getTeacher() {return teacher;}public void setTeacher(Teacher teacher) {this.teacher = teacher;log.info("----student中的setTeacher方法被调用");}}
@Slf4j
@Component
public class Teacher {private String name="李老师";private Student student;public Teacher() {log.info("----teacher的无参数构造方法被执行");}public Teacher(Student student) {this.student = student;log.info("----teacher的有参数构造方法被执行");}public String getName() {return name;}public void setName(String name) {this.name = name;}public Student getStudent() {return student;}public void setStudent(Student student) {log.info("----teacher中的setStudent方法被调用");this.student = student;}
}
@Component
@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("student")) {Student student = (Student) bean;log.info("----student属性注入完成,student.name:" + student.getName());}return bean;}
}
@Component
@Slf4j
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {if (beanName.equals("student")||beanName.equals("teacher")) {log.info("----bean实例化完成,beanName:" + beanName);}return true;}
}
@Testpublic void test4() {log.info("----单元测试执行开始");AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.fanfu");Student bean = context.getBean(Student.class);log.info("----单元测试执行完毕");}
单元测试执行日志

从单元调试执行日志结果来看,teacher先被实例化,然后再调用student的有参数构造方法进行student对象的实例化,同时把teacher对象注入到了student对象里,这和字段注入、setter方法注入的过程明显不同,字段注入、setter方法注入都是student先实例化,然后实例化teacher对象,再把teacher对象注入到student对象里。这是一个非常明显的区别,要加深印象。
构造方法注入的工作原理
这里从工作原理上,深入分析一下构造方法注入和字段注入、setter方法注入的过程的区别在哪里?
1、通过之前的分析,已经知道bean实例化时机是在AbstractAutowireCapableBeanFactory#doCreateBean--->createBeanInstance(),那么先执行createBeanInstance()并观察其返回值,发现student对象实例化好的同时teacher对象也注入到student对象内,这和单元测试的执行日志结果是一致的;

2、那就直接着进入到AbstractAutowireCapableBeanFactory#createBeanInstance(),方法内第一行resolveBeanClass(mbd, beanName),从名字看意思是解析出bean的Class实例对象,接着往下走调用determineConstructorsFromBeanPostProcessors(beanClass, beanName),又引起了我的注意,看过我的这篇文章的小伙伴Springboot扩展点之SmartInstantiationAwareBeanPostProcessor,一定对determineCandidateConstructors()不陌生,这个方法可以决定使用哪个构造器构造 bean,执行结果就是student的有参数构造方法Student(Teacher teacher);再来看下面的autowireConstructor(beanName, mbd, ctors, args)调用,从名字就能看出来是根据构造器自动装配bean;

3、那还等什么,进入AbstractAutowireCapableBeanFactory#autowireConstructor(),里面太简单了,new一个构造方法解析器对象(ConstructorResolver),调用ConstructorResolver#autowireConstructor();
4、ConstructorResolver#autowireConstructor()内的解析过程比较复杂,各种的判断,让人眼花缭乱,但没关系,谁让我是一机灵鬼呢,我发现第一句就是BeanWrapperImpl bw = new BeanWrapperImpl(),这太熟悉了,Spring容器刚实例化好的bean都包装成一个BeanWrapper类型对象,我只需要牢牢看好这个对象,肯定能找到在什么时候注入student对象。后面果然让我抓到了,getUserDeclaredConstructor(candidate)拿到了有参数的构造方法对象,然后调用createArgumentArray(),入参数有bean的Class对象实例、bean构造方法形参名称,这个方法十有八九有我想要的结果;

5、进入到ConstructorResolver#createArgumentArray(),里面又是很多的判断,判断完之后又调用了ConstructorResolver#resolveAutowiredArgument(); 这一块判断比较多,一定要耐心;
这个方法要加印象,一会记得看一下返回值(在第10步);

6、进入到ConstructorResolver#resolveAutowiredArgument(),眼前一亮发现了 this.beanFactory.resolveDependency(...),而这里的this.beanFactory是DefaultListableBeanFactory,有看过Spring依赖注入(一):字段注入的方式是如何工作的?Sprng依赖注入(二):setter注入是如何工作的?的一对这个方法也不会陌生,从名字也能看出来是bean的依赖解析;

7、进入到DefaultListableBeanFactory#resolveDependency(),又调用了doResolveDependency();

8、doResolveDependency()中,又调用了findAutowireCandidates(beanName, type, descriptor)查找Student类依赖属性Teacher类的Class对象;

9、doResolveDependency()中,继续向下会调用descriptor.resolveCandidate(...),而这个方法就是根据上一步得到Teacher类的Class对象实例化Teacher;进入到descriptor.resolveCandidate(...),就一句beanFactory.getBean(beanName),是不是熟悉的配方熟悉的味道?

10、跳过teacher对象的创建过程,继续往下,直到ConstructorResolver#createArgumentArray()执行完,返回值是teacher对象,还记得开始是准备实例化student,但是student还没实例化,其依赖的引用属性teacher已经完成了实例化了;

, java.lang.Object[])实例化Student,拿到student对象再包装进BeanWrapperImpl对象,这里着重看一下instantiate()的入参数是:构造方法对象、teacher对象;

12、至此teacher、student对象都已经实例化了,并且student对象的依赖属性teacher也注入了;继续往下走,直到AbstractAutowireCapableBeanFactory#doCreateBean--->createBeanInstance()执行完;还记得在Spring依赖注入(一):字段注入的方式是如何工作的?是如何分析的吗?如下:
Spring中bean创建核心逻辑都在AbstractAutowireCapableBeanFactory#doCreateBean()中,这个方法主要干了这几件事:第一,bean的实例化;第二,bean的后置处理方法执行;第三,把实例化完成、未完成属性注入的bean提前暴露到Spring的第三级缓存中去;第四,bean依赖属性注入;第五,bean的初始化;student对象在完成这几步后,就可以作为Spring容器中正式的bean开始被使用了。
但是事实上,createBeanInstance()执行完的时候,依赖属性注入已经完成了,第三步注入到Spring第三级缓存里的bean已经是一个完成属性注入的bean了,第四步也不会再执行注入操作了;
构造方法注入总结
Spring bean使用构造方法注入依赖属性的注入时机,要早于字段注入和、setter方法注入,而且注入到Spring第三级缓存里的bean已经是一个完成依赖属性注入的bean。得到这个结论,后面再来分析Spring的bean的循环依赖为什么有的是可以解决的?有的是解决不了的?其实答案已经明显了,下面一篇文章将重点分析。
相关文章:

Sprng依赖注入(三):构造方法注入是如何工作的?
前言这是Spring依赖注入系列的第三篇,前两篇主要分析了Spring bean依赖属性注入的两种方式,是字段注入和setter方法注入,单独比较这两种方式,会发现其过程和工作原理非常类似,那么构造方法注入会不会也和前两种比较类似…...

「1」指针进阶——详解
🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀 目录 🐰指针的回顾 🐰字符指针 🐰指针数组 🌸模…...

JS语法让人困惑的点 “==与===”
在JS中有很多神奇的语法,非常让人困惑,我们就先一一道来,相信你在开发中或多或少都踩过这些坑,或者让人无法理解。 今天我们就来说下【】和【】 这题对于很多没有系统学过前端开发的技术人员来说,算个重点,…...

《狂飙》壁纸大嫂如此惊艳,做成日历壁纸天天看
兄弟们,今年的反腐大剧狂飙都有看吗 ? 话说,名字虽然叫狂飙,但是全剧只有有田一个人在狂飙! 当然,有田虽然亮眼,但是毕竟是个糟老头子,正经人谁看有田啊,当然是看大嫂了…...

手机照片删除了怎么恢复
手机照片删除了怎么恢复?喜欢拍照的小伙伴,都会不定期删除手机上的照片,因为这些爱拍照的人,手机中会存储着很多照片,删除照片是必然的,但在手机删除照片时,如果是一张一张删除太麻烦了,就直接…...

maven pom.xml 依赖的scope属性
maven pom.xml 依赖的scope属性 compile 适用范围 编译期、测试期、运行期 作用 从中央仓库拉取依赖到本地,并编译 打包到结果包中 runtime 适用范围 测试期、运行期 作用 runtime 用在 Class.forName(“com.mysql.jdbc.Driver”) 时,compile 编…...

git 的使用方法 (下 - 远程仓库和图形化)
目录前言:一、什么是协同开发二、Gitee 使用协同开发1. 首先注册一个码云账号2. 新建一个仓库3. 根据下图把新建仓库设置为开源4. 在远端合并分支的方法5. 链接 git 远程6. 提交(同步)远程7. 远程拉取至本地8. 远程分支三、git 图形化的使用1…...

Java基础:拼图小游戏
涉及到的知识: 1.图形用户接口GUI(Graphical User Interface)用图形化的方式显示操作界面 两个体系: AWT包和Swing包 2.界面会用到JFrame类 3.界面中的菜单会用到JMenuBar, JMenu, JMenuItem 4.添加图片 在设置完JLabel的location之后还需要获得展示内容的窗体, 通过setLay…...

一个跟蘑菇结缘的企业老板
记得那是一个很久以前的一家公司了董事长办公室里中的大型盆栽里面长了一个蘑菇董事长认为是祥瑞每天都会浇水后来一个新来的保洁阿姨以为杂草啥的给他掰掉扔垃圾桶了董事长第二天来浇水的时候发现没了就问谁动了他的蘑菇问道之后就跑到楼道大垃圾桶那里把蘑菇找回来种在花盆里…...

【Leetcode 剑指Offer】第 4 天 查找算法(简单)
查找剑指 Offer 03. 数组中重复的数字剑指 Offer 53 - I. 在排序数组中查找数字 I二分法题目链接剑指 Offer 03. 数组中重复的数字 题:在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数…...

Jenkins利用docker部署vue项目
Jenkins利用docker部署vue项目一、环境准备1、安装docker2、安装nodejs3、安装cnpm与配置淘宝镜像4、jenkins安装nodejs插件二、jenkins以vue项目1、全局参数配置2、源码配置3、构建环境4、构建三、构建项目四、访问一、环境准备 本次jenkins与部署vue项目在同一台机器&#x…...

【Linux】如何将ntfs硬盘挂载到home目录下并具有读写权限
步骤1. 查看当前挂载的硬盘及其挂载点2. 查看需要挂载到home下的磁盘类型信息3. 在home下新建一个空的文件夹作为该磁盘的新挂载点4. 以ntfs类型的硬盘为例,使用mount命令进行挂载5. 问题1:进程占用了磁盘6. 问题2:磁盘权限为只读的7. 永久挂…...

拖拽删除元素、拖拽排序、拖拽预览图片和拖拽移动元素
介绍 HTML5 提供了专门的拖拽与拖放的 API,目前各浏览器都已支持,包括 IE。HTML 拖放(Drag and Drop)接口使应用程序能够在浏览器中使用拖放功能。例如,用户可使用鼠标选择可拖拽(draggable)元素…...

yarn的global安装命令不生效
问题 yarn全局安装某个依赖完成之后,但依赖没有生效,一般有两种情况导致的。 解决思路 1.yarn命令问题 yarn在全局安装某个依赖时,global要紧接在yarn之后,然后才是add yarn global add xxxx如果出现global在add之后ÿ…...

如何发布自己的npm包?
1 文件组成 package.json文件components文件css样式文件index.js文件 2 package.json配置 description:描述title:题目keywords:搜索关键词typings:指定TypeScript的入口文件main:加载的入口文件module:…...

达梦数据库 闪回查询
当用户操作不慎导致错误的删改数据时,非常希望有一种简单快捷的方式可以恢复数据。闪回技术,就是为了用户可以迅速处理这种数据逻辑损坏的情况而产生的。 闪回技术主要是通过回滚段存储的 UNDO 记录来完成历史记录的还原。如果提交了,还没有…...

java基础学习 day44(多态的优点和劣势)
1. 多态的优势 在多态形式下,右边对象可以实现解耦合(即之后的代码与右边的子类对象不绑定,在更改子类对象后,之后的代码仍可以使用),便于扩展和维护在定义方法的时候,使用父类型作为参数&…...

Guna UI WinForms 2.0.4.4 Crack
Guna.UI2 WinForms is the suite for creating groundbreaking desktop app UI. It is for developers targeting the .NET Windows Forms platform. 50 多个 UI 控件 具有广泛功能的综合组件可帮助您开发任何东西。 无尽的定制 只需拖放即可创建视觉效果命令和体验。 出色的…...

零售航母沃尔玛公布业绩:喜忧参半
2月21日美股盘前,零售巨无霸沃尔玛公布了截至1月的2023财年第四季度业绩报告。财报中不乏可圈可点之处,但是利润迎来六年首降,新财年的利润指引要也比预期低很多,可以说喜忧参半。 一、Q4业绩可圈可点 营收方面:在本…...

Python学习笔记丨while、for、if循环结构基础知识与易错点
Python流程控制 本篇笔记的主要内容是:条件控制和循环控制,包括if语句、while语句、for语句等。 Python条件控制 if (m : 1) > 0: # :是海象运算符,用于在函数内部为变量赋值 print("ok")ok 通过if语句来判断条件是否成立&am…...

【ROS学习笔记1】ROS快速体验输出Hello World
【ROS学习笔记1】ROS快速体验输出Hello World 文章目录【ROS学习笔记1】ROS快速体验输出Hello World1.1 ROS快速体验1.1.1 Hello World快速实现简介1.1.2 Hello World的C实现1.1.3 Hello World的Python实现写在前面,本系列笔记参考的是AutoLabor的教程,具…...

复习git的使用
文章目录复习git的使用基础提交文件查看回退撤销修改分支创建切换tag其他命令HEAD 指针 的理解复习git的使用 最近公司的老旧项目要由svn转到git,git 命令大都忘记了,这里复习总结一下。 基础 查看本地git版本 git --version 查看本地git配置信息 gi…...

pip命令大全 含换源方法
目录 一、命令列表 二、通用选项列表 三、常用操作 1.使用 requirements.txt 安装包 2.生成requirements.txt文件 3.pip升级命令 4.开启向后不兼容的新功能 5.启用已弃用的功能 四、pip换源 1.临时使用pip源方法 2.永久修改方法 一、命令列表 命令说明实例install安…...

数据结构与算法之最短路路径与最短路径和动态规划
If every unfolding we experience takes us further along in life, then, we are truly experiencing what life is offering.如果我们在人生中体验的每一次转变都让我们在生活中走得更远,那么,我们就真正的体验到了生活想让我们体验的东西。Do not tr…...

git 本地新建分支并进行合并
由于新的要求 不允许在线上直接clone下的git分支进行开发,只能本地新建分支再往线上分支合并远程库clone到本地库 git clone 需要下载的git地址注意我下载下来的是dev分支 根据实际情况进行分析git clone https://gitee.com/hello.git本地创建新的分支 git checkout…...

2023年DAMA-CDGA/CDGP数据治理认证选择哪家机构好?
DAMA认证为数据管理专业人士提供职业目标晋升规划,彰显了职业发展里程碑及发展阶梯定义,帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力,促进开展工作实践应用及实际问题解决,形成企业所需的新数字经济下的核心职业…...

浅析高速服务区交互一体机设备管理系统的建设与方向
很多高速公路服务区均缺乏现代化的服务思维、理念和手段,信息系统功能薄弱,服务区的自助服务终端存在功能单一、人机交互体验差、设备维护管理成本高、联动效率低、运营难等问题,这不仅无法支撑服务区的精细化服务和智能化管理需求࿰…...

分布式面试题
目录 分布式id的生成方案有哪些 雪花算法生成的ID由哪些部分组成 分布式锁在项目中有哪些应用场景? 分布式锁有哪些解决方案 Redis做分布式锁用什么命令 Redis做分布式锁,死锁有哪些情况?如何解决 Redis如何做分布式锁 MySQL如何做分布式锁 什么…...

Prophet 处理时间序列数据
Prophet 处理时间序列数据 flyfish 论文地址 https://peerj.com/preprints/3190/ 官网 https://facebook.github.io/prophet/ 源码地址 https://github.com/facebook/prophet hon import pandas as pd from prophet import Prophet df pd.read_csv(https://raw.githubuse…...

一文搞清楚LoRa网关,LoRa网关全知道
欢迎来到东用知识小课堂下面,今天我们用东用科技的OGC300系列LoRa为例,以简单的方式帮助大家了解一下LoRa相关的小知识一、LoRa网关的基本介绍LoRa是semtech公司创建的低功耗局域网无线标准,低功耗一般很难覆盖远距离,远距离一般功…...