Sprng依赖注入(二):setter注入是如何工作的?

news/2024/4/25 7:46:57/文章来源:https://blog.csdn.net/fox9916/article/details/129170147
文章示例环境配置信息
jdk版本:1.8
开发工具:Intellij iDEA 2020.1
springboot:2.3.9.RELEASE

前言

在Spring依赖注入(一):字段注入的方式是如何工作的?中主要分享了Spring bean依赖注入方式中的字段注入方式及其工作过程,这篇文章来分享一下SPrng bean依赖注入的第二种方式:setter方法注入,从名字来猜也能猜出一个大概,就是通过调用bean的setter方法进行注入,根本原理是java的反射技术。

settter注入方法

以@Autowired注解为例,即把@Autowired注解标记在目标bean的引用bean的setter方法上;

setter注入示例

示例主要内容:1、定义Teachert类; 2、定义Student类;3、在Student类中依赖Teacher;4、使用@Autowired注解标记在Student#setTeacher()上,即在Student对象中以setter方法注入的方式注入Teacher对象;

@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;}
}
@Slf4j
@Component
public class Student {private String name = "小明";private Teacher teacher;public Student() {log.info("----student的无参数构造方法被执行");}public 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;}@Autowiredpublic void setTeacher(Teacher teacher) {this.teacher = teacher;log.info("----student中的setTeacher方法被调用");}}
@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;}
}
@Test
public void test4() {log.info("----单元测试执行开始");AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.fanfu");Student bean = context.getBean(Student.class);log.info("----bean:"+bean.getName());log.info("----单元测试执行完毕");
}

单元测试执行日志:

从执行日志来看,setter方式的依赖注入和字段注入方式基本流程是一样的,student先调用无参数的构造方法进行实例化,然后在student对象内需要注入teacher对象,发现teacher对象未实例化,于是开始实例化teacher对象,待teacher对象实例化完成后(teacher对象内无引用其他bean,其属性注入、bean初始化略过),teacher对象注册到Spring容器中后,又接着student对象实例化后的后续操作,调用student的setTeacher()进行teacher对象的注入。

setter注入的工作原理

1、AbstractAutowireCapableBeanFactory#doCreateBean--->createBeanInstance(),开始student对象的实例化;

2、AbstractAutowireCapableBeanFactory#createBeanInstance()执行完之后,student对象实例化完成,接着往下,在开始依赖属性注入之前,student对象提前暴露至Spring的第三级缓存里,这个时间很重要,也是解决bean循环依赖的关键所在,这里先加深一下印象,后面再细说。另外这里注意一下缓存的key是beanName,但是value不是一个具体的对象,而用lambda表达式写的接口ObjectFactory,ObjectFactory接口里有一个抽象方法getObject(),这里是什么意思呢?ObjectFactory#getObject()方法被触发的时候,会执行lambda表达式的内容; 而lambda表达式里的getEarlyBeanReference()正是从三级缓存里取完成实现化、未属性注入的半成品bean的引用的,这里暂时不涉及循环依赖,待后面的文章里再细说这里。

3、往下执行到AbstractAutowireCapableBeanFactory#populateBean()中,在bean级后置处理器InstantiationAwareBeanPostProcessor的具体实现AutowiredAnnotationBeanPostProcessor,会专门来处理@Autowired和@Value注解标记的依赖属性注入,具体入口方法是AutowiredAnnotationBeanPostProcessor#postProcessProperties();

4、进入到AutowiredAnnotationBeanPostProcessor#postProcessProperties()中,这和字段注入方式是同一个方法,这里就做了两件事:第一、把student对象内需要注入的teacher属性的类型、注入方式、具体执行属性注入的工具类(AutowiredFieldElement)包装成一个InjectionMetadata对象;第二,包装好的InjectionMetadata对象调用自身的inject()完成student对象依赖属性teacher的注入;注意,这个时间student的依赖属性teacher还未实例化呢

5、继续往里面走,进入到这个方法AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement#inject()停一下,是个关键点;AutowiredMethodElement是AutowiredAnnotationBeanPostProcessor的内部类,和AutowiredFieldElement功能比较类似,从名字上就可判断出来,一个是用于字段注入,另一个是setter方法注入;然后在这个方法里调用 resolveMethodArguments(),注意一下,从名字就可以看出来,是用于解析方法的参数的,这里是setter方法注入,setter的参数就是Teacher,因此就可以理解这个方法就是用来解析出student对象的依赖属性teacher对象的;再往下,如果解析出来的参数对象不为空,就使用java反射调用setTeacher()方法进行依赖属性注入;

6、setter方法依赖属性的注入到上面基本上就完成了,但是这里再进入到resolveMethodArguments()里,看看teacher又是如何被搞出来的?debug发现,resolveMethodArguments()又调用 了DefaultListableBeanFactory#resolveDependency();

7、继续跟DefaultListableBeanFactory#resolveDependency(),发现又调用了DefaultListableBeanFactory#doResolveDependency();

9、好像这里也不是teacher实例化重点,继续往下,DefaultListableBeanFactory#doResolveDependency()里又调用了findAutowireCandidates(),从方法名字看是要查找注入的候选对象的,方法的返回值是student对象依赖属性teacher的Class对象,这离teacher的实例化是真不远了;

10、DefaultListableBeanFactory#doResolveDependency()里继续往下走,发现又调用了DependencyDescriptor#resolveCandidate(),从这个返回结果变量的命名上看,意思是实例化候选对象,入参数是注入属性字段的名称teacher,好像就是这了;

11、果断进入到DependencyDescriptor#resolveCandidate(),里面就干了一件事AbstractBeanFactory#getBean(java.lang.String),到这里,如果之前的Springboot扩展点系列文章都有看过,就会明白后面没啥 新鲜事了,就是把bean的实例化、属性初入、初始化等一系列流程;

12、等teacher对象实例化、属性注入、初始化等这一系列流程走完后,就又回到了第5步,使用java反射调用Student#setTeacher()把teacher对象注入到student对象中;

setter注入小结

从流程上来看,字段注入方式和setter方法注入依赖的流程类似,唯一不同的就是具体执行注入操作的工具类不同;其实最应该被学习和记住的就是,spring依赖注入过程中关键方法的命名方式,真的是见名知义呀,如果业务开发里也能这样,阅读自己和别人的的代码将是一件轻松愉快的事情。

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Pytorch,从头开始实现Transformer(编码器部分)

Transformer理论部分参考知乎上的这篇文章 Transformer的Attention和Masked Attention部分参考知乎上的这篇文章 Transformer代码实现参考这篇文章,不过这篇文章多头注意力实现部分是错误的,需要注意。 完整代码放到github上了,链接 Trans…

联想小新 Air-14 2019IML电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网,转载需注明出处。硬件型号驱动情况主板Lenovo LNVNB161216处理器Intel Core i5-10210U / i7-10510U已驱动内存8GB DDR4 2666已驱动硬盘康佳KAK0500B128(128 GB/固志硬盘)已驱动显卡Intel UHD 620Nvidia GeForce MX250(屏蔽)无法驱动声卡Cone…

轮播图、阅读注册协议、网页时钟、随机点名、小米搜索框、轮播图点击切换——web APIs练习

目录 一、获取元素(DOM) 1. 随机轮播图案例 2. 阅读注册协议(定时器间歇函数的应用) 3. 轮播图定时器版 4. 网页时钟 二、事件基础(DOM) 1. 随机点名案例 2. 轮播图点击切换(重点&#…

【计算机网络 -- 期末复习】

例题讲解 IP地址(必考知识点) 子网掩码 子网划分 第一栗: 子网划分题目的答案一般不唯一,我们主要采用下方的写法: 第二栗: 路由跳转 数据传输 CSMA/CD数据传输 2、比特率与波特率转换 四相位表示&am…

ElasticSearch 学习笔记总结(一)

文章目录一、 数据的 分类二、 ElasticSearch 介绍三、 ElasticSearch 搭建四、正排索引 和 倒排索引五、ES HTTP 索引 操作六、ES HTTP 文档 操作七、ES HTTP 查询数据1. 条件查询2. 分页查询3. 排序查询4. 多条件查询5. 全文检索 完全匹配 高亮显示6. 聚合查询八、 ES HTTP 映…

Scalable but Wasteful: Current State of Replication in the Cloud

文章目录ABSTRACT1 INTRODUCTION2 REPLICATION IN THE WILD3 CURRENT APPROACHES TO SCALING STATE MACHINE REPLICATION4 EFFICIENCY METRIC5 INEFFECTIVENESS OF CURRENT APPROACHES PER NEW METRIC6 CONCLUSION AND FUTURE DIRECTIONSABSTRACT 共识协议是部署在基于云的存储…

面试热点题:stl中vector与list的优缺点对比、以及list的迭代器与vector迭代器的区别

vector的优点 下标随机访问 vector的底层是一段连续的物理空间,所以支持随机访问尾插尾删效率高 跟数组类似,我们能够很轻易的找到最后一个元素,并完成各种操作cpu高速缓存命中率高 因为系统在底层拿空间的时候,是拿一段进cpu&am…

Linux:基于libevent读写管道代码,改进一下上一篇变成可以接收键盘输入

对上一篇进行改进&#xff0c;变成可以接收键盘输入&#xff0c;然后写入管道&#xff1a; 读端代码&#xff1a; #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <s…

乌卡时代的云成本管理:从0到1了解FinOps

在上一篇文章中&#xff0c;我们介绍了企业云业务的成本构成以及目前面临的成本困境&#xff0c;以及当前企业逐步转向 FinOps 的行业趋势&#xff0c;这篇文章我们将详细聊聊 FinOps&#xff0c;包括概念、重要性以及成熟度评价指标。 随着对云服务和供应商的使用越来越多&…

Sms多平台短信服务商系统~完成阿里云短信服务发送可自行配置

1.项目中引入Maven 阿里云地址 不同编程语言都有对应的SDK,你们下载自己需要的即可。 pom.xml中添加maven坐标 <!--阿里云短信服务--><dependency><groupId>com.aliyun</groupId><artifactId>alibabacloud-dysmsapi20170525</artifactId>…

【UE4 制作自己的载具】1-使用3dsmax制作载具

学习谌嘉诚课程所做笔记源视频链接&#xff1a;【虚幻4】UE4雪佛兰科迈罗汽车详细制作中文教程&#xff01;&#xff08;汽车骨骼绑定驾驶、动画蓝图&#xff09;汽车模型下载链接&#xff1a;https://pan.baidu.com/s/1ZH5gaAwckzRIZ0w6n0qvIA密码&#xff1a;19sj步骤&#x…

系列五、事务

一、事务简介 1.1、定义 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系 统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 例如: 张三给李四转账1000块钱&#xff0c;张…

Codeforces Round #848 (Div. 2)(A~D)

A. Flip Flop Sum给出一个只有1和-1的数组&#xff0c;修改一对相邻的数&#xff0c;将它们变为对应的相反数&#xff0c;修改完后数组的和最大是多少。思路&#xff1a;最优的情况是修改一对-1&#xff0c;其次是一个1一个-1&#xff0c;否则修改两个1。AC Code&#xff1a;#i…

2023-02-22 学习记录--TS-邂逅TS(二)

TS-邂逅TS&#xff08;二&#xff09; 不积跬步&#xff0c;无以至千里&#xff1b;不积小流&#xff0c;无以成江海。&#x1f4aa;&#x1f3fb; 一、接口&#xff08;interface&#xff09; 在 ts 中&#xff0c;子类只能继承一个父类&#xff0c;不可多继承&#xff0c;但是…

学习国家颁布的三部信息安全领域法律,理解当前工作中的信息安全合规要求

目录三部信息安全领域的法律文件三部法律的角色定位与联系三部法律的适用范围三部法律的主要履职部门三部法律条文章节结构中的共性三部法律中的一些次重点章节网络安全法的重点章节数据安全法的重点章节个人信息保护法的重点章节关于工业和信息化部行政执法项目清单三部信息安…

ChatGPT这是要抢走我的饭碗?我10年硬件设计都有点慌了

前 言 呃……问个事儿&#xff0c;听说ChatGPT能写电路设计方案了&#xff0c;能取代初级工程师了&#xff1f;那我这工程师的岗位还保得住么&#xff1f;心慌的不行&#xff0c;于是赶紧打开ChatGPT问问它。 嘿&#xff0c;还整的挺客气&#xff0c;快来看看我的职业生涯是否…

非关系型数据库(mongodb)简单使用介绍

关系型数据库与非关系型数据库 关系型数据库有mysql、oracle、db2、sql server等&#xff1b; 关系型数据库特点&#xff1a;关系紧密&#xff0c;由表组成&#xff1b; 优点&#xff1a; 易于维护&#xff0c;都是使用表结构&#xff0c;格式一致&#xff1b; sql语法通用&a…

IP地理位置定位技术原理是什么

IP地理位置定位技术的原理是基于IP地址的网络通信原理和基础上的。它利用IP地址所包含的一些信息&#xff0c;如网络前缀和地址段&#xff0c;以及ISP的IP地址归属地数据库&#xff0c;来推测IP地址所对应的地理位置。具体来说&#xff0c;IP地址是由32位二进制数字组成的&…

《计算机网络:自顶向下方法》实验2:常用网络命令的使用

使用Ping实用程序来测试计算机的网络连通性 登录到Windows中。单击开始,然后将鼠标指针移到程序上,再移到Windows系统,然后单击命令提示符。在命令提示窗口键入ping 127.0.0.1。问题1:发送了多少数据包?接受了多少数据包?丢失了多少数据包? 发送了4个数据包;接受了4个数…

Java集合(二)---Map

1.什么是Hash算法哈希算法是指把任意长度的二进制映射为固定长度的较小的二进制值&#xff0c;这个较小的二进制值叫做哈希值static final int hash(Object key) {int h;return (key null) ? 0 : (h key.hashCode()) ^ (h >>> 16);}以上是HashMap中的hash算法代码2…