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

news2024/11/27 0:26:12
文章示例环境配置信息
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;
    }

    @Autowired
    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
        log.info("----student中的setTeacher方法被调用");
    }

}
@Component
@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public 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 {
    @Override
    public 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.coloradmin.cn/o/364771.html

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

相关文章

数据结构与算法之最长公共子序列动态规划

目录:一.题目及其示例二.动态规划的基本思想三.思路动态规划五部曲1.确定dp数组(dp table)以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例推导dp数组一.题目及其示例给定两个字符串 text1 和 text2,返回这两…

Docker----------day5---安装redis集群

1.哈希取余分区 2亿条记录就是2亿个k,v,我们单机不行必须要分布式多机,假设有3台机器构成一个集群,用户每次读写操作都是根据公式: hash(key) % N个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上。…

华为机试题:HJ81 字符串字符匹配(python)

文章目录(1)题目描述(2)Python3实现(3)知识点详解1、input():获取控制台(任意形式)的输入。输出均为字符串类型。1.1、input() 与 list(input()) 的区别、及其相互转换方…

VO,BO,PO,DO,DTO,AO的区别

DTO(Data Transfer Object)数据传输对象 这个传输通常指的前后端之间的传输 1.在前端的时候: 存在形式通常是js里面的对象(也可以简单理解成json),也就是通过ajax请求的那个数据体 2.在后端的时候&…

深入浅出C++ ——map类深度剖析

文章目录一、map类介绍二、map的使用三、multimap一、map类介绍 map是 C STL 中提供的容器,map是数学上的映射,其具有唯一性,即每个pair(key,value)只出现一次,而 multimap 则是可重复映射,两者的内部实现是一棵红黑树…

[Datawhale][CS224W]图机器学习(五)

这里写目录标题一、Deepwalk1.1 预备知识1.2 Deepwalk介绍1.3 Embedding1.4 word2Vec 词向量,词嵌入1.5 random Walk随机游走1.6 DeepWalk 核心代码Random WalkWord2vecDeepWalk应用1.7 DeepWalk优缺点二、Node2Vec2.1 图嵌入2.2 Node2Vec优化目标顶点序列采样策略2…

固态继电器控制电路

固态继电器控制电路 固态继电器(SSR)的种类和型号很多,因此其输入控制方法和控制电路也相应众多。固态继电器(SSR)的共同特点在于驱动电流或驱动电压小,即只需输入一个小信号即可控制SSR的开关。 如果需要…

瞎更新,container_cpu_usage_seconds_total{job=“cadvisor“} 怎么没有啦

一、 基本介绍 1.1 概论 1.1.1 故事背景 今天在同步其他团队的 grafana 监控大盘时,Prometheus 服务报告说不能找到名为 container_cpu_usage_seconds_total{job“cadvisor”} 的指标,一般来说可能有几个原因。 可能是 Prometheus 服务没开启prometh…

leaflet 实现极地标线地图,加载tileLayer.wms数据(088)

第088个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中实现极地标线地图,加载tileLayer.wms数据。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共93行)安装插件专栏目标相关API参考:示例效果…

什么是API?(详细解说)

编程资料时经常会看到API这个名词,网上各种高大上的解释估计放倒了一批初学者。初学者看到下面这一段话可能就有点头痛了。 API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开…

超详细的阿里java岗社招知识点整理,常考知识点全在这里了

常考知识点 1、java的基本数据类型与包装类; 2、final修饰变量类方法; 3、String为什么是不可变的,以及new String(“abc”)创建了几个对象; 4、String、StringBuffer、以及StringBuilder的区别; 5、static修饰变…

金融BI分析的价值

众所周知,金融行业从信息化时代起,就是对IT依赖程度非常高的,也是最严苛的。进入到数字化时代,金融机构越来越注重技术创新,BI工具也越来越受到重视。利用BI工具,金融机构可以更快、更准确地获得有关市场信…

2023年1月用户体验GX评测:商业银行抢抓新春营销旺季,多措并举持续提升用户体验

易观:2023年1月正值中国传统春节营销旺季,各家银行充分发挥手机银行的优势,积极探索新春营销新模式,为客户提供极富特色的“开门红”线上营销活动,提升用户参与积极性,在用户体验上积极探索,增强…

真香!Linux 原来是这么管理内存的

Linux 内存管理模型非常直接明了,因为 Linux 的这种机制使其具有可移植性并且能够在内存管理单元相差不大的机器下实现 Linux,下面我们就来认识一下 Linux 内存管理是如何实现的。 一,基本概念 每个 Linux 进程都会有地址空间,这…

基于莱维飞行改进粒子群优化的长短期神经网络ispso-lstm的客流量预测,ISPSO-LSTM,神经网络1000案例之16

目录 摘要 背影 改进思路 shubett测试函数 函数代码 函数图像 测试ispso算法性能 测试代码 测试效果图像 ispso-lstm客流量预测 代码 测试效果 分析 展望 摘要 针对基本粒子群算法容易发生早熟收敛,陷入局部最优,等缺点,提出了基于莱维飞行的改进粒子群算法.在粒子位置更新公…

《计算机网络:自顶向下方法》实验5:TCP

Q1 包含HTTP POST消息的TCP报文段的序号是多少?注意:为了发现POST 命令, 你需要在wireshark底部的报文内容域窗口中去查找,查找数据中包含 “POST”的段。 如图所示,由报文中的POST 和 HTTP/1.1可知,其包含HTTP POST消息; TCP报文段的序号可见TCP报文: Sequence Number:…

win10输入法设置在哪里?对于输入法设置的图文教程

使用电脑打字,那么我们就会用到输入法。对于Windows电脑输入法的设置,很多用户都不知道如何调整。实际上,输入法设置就在“语言”选项里面,想要知道win10输入法设置的操作步骤,那就跟着小编的步伐来看看吧!…

win10下 WSL2安装及配置

目录 一. Windows中WSL2(子系统)安装前提条件 二. Windows中WSL2(子系统)安装步骤(默认安装C盘) 选择包安装模式(选择到其他盘安装) 三. Windows中WSL2(子系统)设置默认root用户登…

数据结构——复杂度的详解(空间复杂度和时间复杂度)

1.程序算法效率 1.1什么是算法? 算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。 1.2衡量算法好坏的标准 这是一个通过函数递归算法实…

轻量级权限框架之-Sa-ToKen

轻量级的权限认证框架 Sa-Token 目前主要五大功能模块:登录认证、权限认证、单点登录、OAuth2.0、微服务鉴权 登录认证 —— 单端登录、多端登录、同端互斥登录、七天内免登录 权限认证 —— 权限认证、角色认证、会话二级认证 Session会话 —— 全端共享Session、…