当前位置: 首页 > news >正文

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

《狂飙》壁纸大嫂如此惊艳,做成日历壁纸天天看

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

手机照片删除了怎么恢复

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

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之后&#xff…...

如何发布自己的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…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...