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.找到想要导出的图纸标高或者立面,例如&…...
DroidRun:用自然语言指令重塑Android自动化体验
1. 当Android遇上自然语言:DroidRun如何重新定义自动化 还记得第一次用语音助手控制手机时的惊艳吗?说句话就能定闹钟、发消息,感觉像在演科幻片。但很快你就会发现,这些功能就像快餐店的固定套餐——只能点菜单上有的,…...
【英飞凌】TC3XX单片机型号解码:从命名规则看芯片选型
1. 英飞凌TC3XX单片机命名规则解析 第一次接触英飞凌TC3XX系列单片机时,我完全被那一长串型号搞懵了。TC387TP、TC377T、TC397QP...这些看似随机的字母数字组合,其实隐藏着丰富的芯片信息。经过几个项目的实战,我终于摸清了这套命名规则的规律…...
探索Tabler Icons 3.40.0:新增6000+高质量SVG图标的终极指南
探索Tabler Icons 3.40.0:新增6000高质量SVG图标的终极指南 【免费下载链接】tabler-icons A set of over 4800 free MIT-licensed high-quality SVG icons for you to use in your web projects. 项目地址: https://gitcode.com/GitHub_Trending/ta/tabler-icons…...
避坑指南:HuggingFace本地数据集加载常见的5个报错及解决方法
HuggingFace本地数据集加载实战:5类典型报错深度解析与解决方案 当你第一次尝试将本地数据集加载到HuggingFace生态系统中时,可能会遇到各种令人困惑的错误信息。这些报错往往隐藏着数据格式、特征定义或路径处理等关键问题。本文将剖析开发者最常遇到的…...
OpenClaw任务编排:用Qwen3.5-4B-Claude实现爬虫+分析闭环
OpenClaw任务编排:用Qwen3.5-4B-Claude实现爬虫分析闭环 1. 为什么需要自动化任务编排 去年我接手了一个市场调研项目,需要每周从20多个网站抓取产品价格数据,清洗后生成趋势图表。最初用Python脚本手动Excel处理,每次要花3小时…...
手把手教你用ThinkPHP6和Uniapp从零搭建一个物业设备巡检小程序(附完整源码)
从零构建物业设备巡检系统:ThinkPHP6与Uniapp全栈实战指南 物业设备巡检是保障设施安全运行的关键环节,传统纸质记录方式效率低下且难以追溯。本教程将带您从零开始,基于ThinkPHP6后端框架与Uniapp跨端方案,构建一个功能完整的移动…...
3步实现!本地化语音转文字工具TMSpeech全场景应用指南
3步实现!本地化语音转文字工具TMSpeech全场景应用指南 【免费下载链接】TMSpeech 腾讯会议摸鱼工具 项目地址: https://gitcode.com/gh_mirrors/tm/TMSpeech 在数字化办公与内容创作领域,如何在保护隐私的前提下实现高效语音转文字?TM…...
Fillinger智能填充脚本终极指南:如何快速实现图形元素的智能分布
Fillinger智能填充脚本终极指南:如何快速实现图形元素的智能分布 【免费下载链接】illustrator-scripts Adobe Illustrator scripts 项目地址: https://gitcode.com/gh_mirrors/il/illustrator-scripts Fillinger是一款专为Adobe Illustrator设计的智能填充脚…...
HTML网页元素中的图片和超链接
哈哈哈,又来更新我这一周里面新学的web前端开发技术啦!今天我将与大家分享网页元素中的图片和超链接。一.图像的应用HTML中加入图片有3种不同的路径:1.绝对路径:是指互联网上唯一且完整的地址,用来精准定位资源。绝对路…...
3分钟搞定!LyricsX让你的macOS音乐播放器拥有完美歌词体验
3分钟搞定!LyricsX让你的macOS音乐播放器拥有完美歌词体验 【免费下载链接】LyricsX 🎶 Ultimate lyrics app for macOS. 项目地址: https://gitcode.com/gh_mirrors/ly/LyricsX 还在为macOS上的音乐播放器找不到合适的歌词而烦恼吗?L…...
