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…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...
pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...
Qt的学习(一)
1.什么是Qt Qt特指用来进行桌面应用开发(电脑上写的程序)涉及到的一套技术Qt无法开发网页前端,也不能开发移动应用。 客户端开发的重要任务:编写和用户交互的界面。一般来说和用户交互的界面,有两种典型风格&…...
GraphRAG优化新思路-开源的ROGRAG框架
目前的如微软开源的GraphRAG的工作流程都较为复杂,难以孤立地评估各个组件的贡献,传统的检索方法在处理复杂推理任务时可能不够有效,特别是在需要理解实体间关系或多跳知识的情况下。先说结论,看完后感觉这个框架性能上不会比Grap…...
