Spring之实例化Bean _ @Resource和@Autowired实现原理(3)
目录
1. 搜集注解信息 applyMergedBeanDefinitionPostProcessor(*)
2. 将实例化的Bean放入3级缓存中 addSingletonFactory(***)为循环依赖做准备
3. 根据搜集的注解进行依赖注入 populateBean(***)
至此,@Autowired 和 @Resource实现DI功能全部说完了。可以说,他们两个对于变量的依赖注入,逻辑几乎一模一样。
4. 最后是对Bean进行初始化操作。initializeBean(****)
在上一篇Spring之实例化Bean(2)_chen_yao_kerr的博客-CSDN博客中,我已经基本上梳理出了Spring实例化Bean的全部流程。但是那一篇是以最简单的对象作为解释的,目的就是通俗易懂。而这一篇是基于上一篇继续做一些更为深入的分析的。
本章节主要是针对@Resource和@Autowired这两个注解的实现展开,因为这两个注解就是Spring IOC中依赖注入的核心。上一篇中实例化Bean完成以后,后面有几个方法我只是简单的带了过去。而这一次,我会聚焦它们。
在IOC中,我们主要就是交给Spring去实例化Bean,然后将Bean进行依赖注入。上一篇Spring之实例化Bean(2)_chen_yao_kerr的博客-CSDN博客我们主要就是围绕实例化Bean讲解的。本篇就是实例化Bean以后,我们还需要进行依赖注入操作,其实依赖注入大体上可以分为4个部分,分别是:
1. 搜集注解信息 applyMergedBeanDefinitionPostProcessors(***)
2. 将实例化的Bean放入3级缓存中 addSingletonFactory(***)为循环依赖做准备
3. 根据搜集的注解进行依赖注入 populateBean(***)
4. 最后是对Bean进行初始化操作。initializeBean(****)
上一篇我们是最简单的Dao对象实例化,而今天的主角是MyTestBean2。它将使用@Resource和@Autowired分别注入Dao和Dao2. 并且依旧使用@PostConstruct完成初始化变量的作用,顺便看看是先进行依赖注入,还是先初始化Bean。
Dao类:
package com.xiangxue.jack.bean;import org.springframework.stereotype.Repository;import javax.annotation.PostConstruct;@Repository
public class Dao {private String name;private String id;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getId() {return id;}public void setId(String id) {this.id = id;}@PostConstruct //相当于init-methodvoid init () {id = "001";name = "yy";}@Overridepublic String toString() {return "name :" + name + " id :" + id;}
}
Dao2类:这个类是我强行实例化BeanDefinition的,因此它没有注解,不用在spring.xml中配置<bean>,依旧可以被Spring实例化,不懂可以看Spring_让Spring 依赖注入彻底废掉_chen_yao_kerr的博客-CSDN博客
public class Dao2 {private String name;private String id;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getId() {return id;}public void setId(String id) {this.id = id;}@Overridepublic String toString() {return "name :" + name + " id :" + id;}
}
主角MyTestBean2类:
package com.xiangxue.jack.bean;import com.xiangxue.jack.postProcessor.Dao2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import javax.annotation.PostConstruct;
import javax.annotation.Resource;@Service
public class MyTestBean2 {@Autowiredprivate Dao dao;@Resourceprivate Dao2 dao2;private String name;public void system () {System.out.println("测试@PostConstruct初始化name-----> :" + name);System.out.println("测试@Autowired注入Dao-----> :" + dao.toString());System.out.println("测试@Resource注入Dao2-----> :" + dao2.toString());}@PostConstructpublic void writeName () {name = "test init-method is after populateBean";}
}
1. 搜集注解信息 applyMergedBeanDefinitionPostProcessor(*)
在Spring之基于注解方式实例化BeanDefinition(1)_chen_yao_kerr的博客-CSDN博客一文中,我们提到过registerComponents是注册一些BeanPostProcessor接口的。而这些接口就是今天的主角。注解信息的搜集,DI注入,对Bean进行初始化都依靠这些接口。接下来我将会直接提到对象的PostProcessor接口,不会再累赘说明这些接口是哪里来的。
首先,我们还是进入AbstractAutowireCapableBeanFactory类的doCreateBean方法中。此时,我们已经实例化完了Bean操作。聚焦于applyMergedBeanDefinitionPostProcessor方法:

而这个方法内部,其实就是调用我们之前注册的BeanPostProcessor的postProcessMergedBeanDefinition方法而已:

我们再次列举一下不同的接口所支持的注解信息:
AutowiredAnnotationBeanPostProcessor 支持@Autowired @Value
CommonAnnotationBeanPostProcessor 支持 @PostConstruct @PreDestroy @Resource
首先,我们关注一下CommonAnnotationBeanPostProcessor ,看看它是如何搜集@PostConstruct @PreDestroy @Resource信息的。

既然是分开搜集的,那我们就先看@PostConstruct @PreDestroy的搜集过程:

我们遇到了熟悉的代码结构了:之前是lamadba表达式 getSingleton(beanName, () ->{****** createBean() *******}),而这次是换成匿名类,原理是一样的。
1,首先调用doWithLocalMethods方法,内部肯定是回调进入method匿名类方法中
2. 在这个方法中,我们会把搜集到的有@PostConstruct @PreDestroy 的方法分别放入currInitMethods 和 curDestoryMethods 集合中
3. 最后再把他们放入 initMethods 和 destoryMethods 集合中。请记住这两个集合的名字

而在doWithLocalMethods方法内部,就是一个反射调用:

4. 最终返回的是一个Metadata, 把init-method方法和destroy-method方法分别放入各自的集合中。这样就搜集完成了。

接下来再看它是如何搜集@Resource注解的:请重点记住injectionMetadataCache这个集合名称。

关注一下具体的搜集过程: 还是熟悉的代码结构。
因为@Resource可以在变量上使用,也可以在方法上使用。所以,我们需要分别搜集

到此为止,我们的CommonAnnotationBeanPostProcessor 搜集注解信息工作就完成了。重要的信息就是3个集合。 initMethods 、destoryMethods和injectionMetadataCache
接下来,我们再看看AutowiredAnnotationBeanPostProcessor是如何搜集信息的,我不用看代码,猜测大体流程应该基本相同。接下来重点关注一下@Autowired 在变量上使用的情况。因为,这样的使用情况比较多



最终我们发现,@Autowired注解的搜集过程和@Resource的搜集过程,基本上是完全一样的代码逻辑。
2. 将实例化的Bean放入3级缓存中 addSingletonFactory(***)为循环依赖做准备

缓存会在讲循环依赖的时候具体分析,此处只要知道有3级缓存,而且实例化Bean以后首先放入3级缓存即可。
3. 根据搜集的注解进行依赖注入 populateBean(***)

debug进入populateBean方法,看看它是如何进行依赖注入的。以下这段代码,我们在Spring_让Spring 依赖注入彻底废掉_chen_yao_kerr的博客-CSDN博客一文中作为甜点分享过了,我们是可以通过这段代码逻辑,自己实现一个InstantiationAwareBeanPostProcessor让Spring的依赖注入功能彻底报废的。

在 populateBean方法内部,继续debug往下:我们发现代码再次调用了PostProcessor接口

因为我们只关注@Resource和@Autowired这两个注解,所以我们继续去看看AutowiredAnnotationBeanPostProcessor 和
CommonAnnotationBeanPostProcessor,看看她们是怎么实现的。
首先,我们看看CommonAnnotationBeanPostProcessor是如何实现@Resource注解


果真是直接从缓存中拿到的metadata数据,那么我们继续看看它是如何设置值的。

看看具体是如何设置值的:

很简单,就是通过反射的形式,将变量dao2,类实例MyTestBean2,通过反射的形式给dao2注入值。但是,这个dao2的值是如何来的呢? 这个地方就和循环依赖扯上关系了。不过,我们这一次不说循环依赖,只看它是如何获取到dao2这个对象的。进入方法内部:
模板设计模板,再次进入CommonAnnotationBeanPostProcessor的内部类ResourceElement中

继续跟进:



最后,还是返回到反射调用处,直接给变量dao2赋值,至此@Resource注解全部流程结束。

接下来该轮到AutowiredAnnotationBeanPostProcessor 实现@Autowired注解的流程了。在阅读源码之前,大胆猜测一下,整体流程基本相同。

接下来看看具体拿metadata过程是否相同:

再来看看注入dao的过程是否相同

进入这个方法以后,我们发现拿值的逻辑是不同的


进入doResolveDependency方法内部:



上面几个方法,做了一些列的逻辑判断,这个是比@Resource注解复杂一些。但是,最后,我们发现,它还是调用了getBean() 方法,也就是说还是要进行实例化的操作。搞了半天,最终才发现,@Autowired 和 @Resource实现逻辑几乎一模样,唯一的不同就是拿变量的对象过程中,逻辑判断稍微有些区别。

至此,@Autowired 和 @Resource实现DI功能全部说完了。可以说,他们两个对于变量的依赖注入,逻辑几乎一模一样。
4. 最后是对Bean进行初始化操作。initializeBean(****)
实例化+ioc依赖注入完以后的调用,简单概括就是对象实例化完成以后,里面的变量,无论是通过注解注入的,还是调用什么方法初始化的,此时都没有值。看下图:

而在调用完initializeBean方法以后,我们可以确认,它已经初始化完成。

其实,它的实现原理,我在 Spring之实例化Bean(2)_chen_yao_kerr的博客-CSDN博客一文中已经解释过了,就是根据之前搜集到的注解信息,找到对应的方法名称,然后通过反射,调用初始化方法,完成变量的初始化操作。具体debug过程可以直接在Spring之实例化Bean(2)_chen_yao_kerr的博客-CSDN博客中搜索 “ initializeBean ”
最终测试结果可以在控制台打印,测试通过:

本文的重点就是populatedBean方法,而循环依赖其实就是基于这个方法完成的。理解这篇文章,那循环依赖就是非常简单的事情了
相关文章:
Spring之实例化Bean _ @Resource和@Autowired实现原理(3)
目录 1. 搜集注解信息 applyMergedBeanDefinitionPostProcessor(*) 2. 将实例化的Bean放入3级缓存中 addSingletonFactory(***)为循环依赖做准备 3. 根…...
华为HCIE学习之Openstack Cinder组件(cinder对接glusterfs)
文章目录一、MQ的作用二、cinder架构图三、各组件的作用四、cinder对接glusterfs一、MQ的作用 服务内各组件交互通过MQ进行 二、cinder架构图 IET,Linux用软件做存储,CNA识别过去就是IETTGT,物理存储,CNA识别过去就是TGT 三、…...
关于Go语言的底层,你想知道的都在这里!
文章目录1. GoLang语言1.1 Slice1.2 Map1.3 Channel1.4 Goroutine1.5 GMP调度1.6 垃圾回收机制1.7 其他知识点2. Web框架Gin和微服务框架Micro2.1 Gin框架2.2 Micro框架2.3 Viper2.4 Swagger2.5 Zap2.6 JWT文章字数大约1.95万字,阅读大概需要65分钟,建议…...
每日一问-ChapGPT-20230308-关于技术与思考的问题
文章目录每日一问-ChapGPT系列起因每日一问-ChapGPT-20230308-关于技术与思考的问题matplotlib_venn 中 venn2函数调用时,subsets传入A list (or a tuple) containing two set objects,怎么理解plt.pie() 包含哪些参数,以及每个参数的意义mat…...
Oracle表分区的创建、新增、拆分
Oracle中为了方便管理、查询数据当数据量大于500w或者2G时最好用分区表,常见的一种是使用时间作为分区。 分区表添加新的分区有 2 种情况: (1) 原分区里边界是 maxvalue 或者 default。 这种情况下,我们需要把边界分区 drop 掉,加…...
如何快速升级Java 8 到Java11
老板让我把一个项目从 Java 8 迁移到 Java 11,我该怎么办呢? 最简单的办法,当然是直接强行升级,遇到一个错就改一个错,别看它 low,但是对于一个小型且非核心的项目来说,已经足够了。 当然,对于比较重要的项目,且代码行数不少的情况,最标准的姿势就是对着官方文档进…...
内卷把同事逼成了“扫地僧”,把Git上所有面试题整理成足足24W字Java八股文
互联网大厂更多的是看重学历还是技术?毫无疑问,是技术,技术水平相近的情况下,肯定学历高/好的会优先一点,这点大家肯定都理解。说实话,学弟学妹们找工作难,作为面试官招人也难呀!&am…...
【计组】主存储器有关知识梳理
一、主存储器 主存储器可以直接和CPU进行通信,但是只能保存临时数据,在断电后数据就消失。还有一个特点是,主存储器的容量小,速度快,造价高。 1.构成 2.主存中存储体的构造 最小的存储单位是存储元,存储元…...
QT对象树
对象模型(对象树) 在Qt中创建对象的时候会提供一个Parent对象指针,下面来解释这个parent到底是干什么的。 l QObject是以对象树的形式组织起来的。 n 当你创建一个QObject对象时,会看到QObject的构造函数接收一个QObject指针作…...
什么是B+树
B树是一种树数据结构。B树索引是B树在数据库中的一种实现,是最常见也是数据库中使用最为频繁的一种索引。 先来了解一下什么是索引? 一、索引 数据都是存储在硬盘上的,查询数据不可避免的需要进行IO操作。 索引是一种数据结构,…...
【Unity游戏破解】外挂原理分析
文章目录认识unity打包目录结构游戏逆向流程Unity游戏攻击面可被攻击原因mono的打包建议方案锁血飞天无限金币攻击力翻倍以上统称内存挂透视自瞄压枪瞬移内购破解Unity游戏防御开发时注意数据安全接入第三方反作弊系统外挂检测思路狠人自爆实战查看目录结构用il2cpp dumper例子…...
windows 关闭指定端口进程
1、首先打开cmd 注意要用管理员身份打开cmd,否则可能出现无权访问的提示。 2、输入以下命令(以端口号9098为例) 查看端口信息 netstat -ano | findstr 90983、输入以下命令关闭这个进程 taskkill -PID 39716 -F...
虚拟化系列教程:创建 KVM 虚机的几种方式
虚拟化系列教程:创建虚拟机的几种方式[TOC](虚拟化系列教程:创建虚拟机的几种方式)创建 KVM 虚机的几种方式使用 virt-install 命令创建虚拟机参数说明一般选项安装方法存储配置网络配置其它常用的选项图形配置设备选项虚拟化平台其它创建虚拟机的操作演…...
MacBook安装Golang Oracle数据库驱动程序
Golang连接Oracle 需要安装Oracle Full Client或Instant Client Oracle的Instant Client套件下载地址 #选择Instant Client for macOS (Intel x86)下载包如下: instantclient-basic-macos.x64-19.8.0.0.0dbru.zip instantclient-sdk-macos.x64-19.8.0.0.0dbru.zip instantcli…...
Elasticsearch 核心技术(七):IK 中文分词器的安装、使用、自定义字典
❤️ 博客主页:水滴技术 🚀 支持水滴:点赞👍 收藏⭐ 留言💬 🌸 订阅专栏:大数据核心技术从入门到精通 文章目录一、安装 IK 分词器方式一:自行下载并解压安装包方式二:…...
【LeetCode】剑指 Offer(19)
目录 题目:剑指 Offer 36. 二叉搜索树与双向链表 - 力扣(Leetcode) 题目的接口: 解题思路: 代码: 过啦!!! 写在最后: 题目:剑指 Offer 36. …...
吐血整理,web自动化测试,POM模式搭建自动化测试框架(超级详细)
目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 POM设计模式 主要是…...
【数据库原理复习】索引 视图 sql语句
这里写目录标题视图视图特点视图定义优点索引相关sql三种索引区别解释视图 视图特点 只是虚表,并不实际存放数据,所有数据都来自于基本表建立在一个或几个基本表或视图之上基本表数据变化视图也随之变化只保存视图定义等之类东西 视图定义 # 定义视图…...
【HDFS】IPC重试
1、IPC重试和dfs.client.retry重试的区别2、IPC重试的相关参数汇总及含义3、 IPC重试相关源码、原理简单总结一句话: IPC重试是因为连接问题而进行重试; 客户端重试是因为RPC在服务端处理发生异常,客户端根据指定的策略进行重试。 接下来让我们深入一下源码,因为每一部分源…...
Revit导出CAD图纸操作及批量导出
一、Revit如何导出CAD格式图纸 1.打开Revit模型。 2.项目浏览器,图纸(全部),鼠标右键点击,新建图纸。 3.选择自己需要的图纸大小,点击“确定”,即可创建一张图纸。 4.找到想要导出的图纸标高或者立面,例如&…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
