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

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方法,而循环依赖其实就是基于这个方法完成的。理解这篇文章,那循环依赖就是非常简单的事情了

http://www.lryc.cn/news/35407.html

相关文章:

  • 华为HCIE学习之Openstack Cinder组件(cinder对接glusterfs)
  • 关于Go语言的底层,你想知道的都在这里!
  • 每日一问-ChapGPT-20230308-关于技术与思考的问题
  • Oracle表分区的创建、新增、拆分
  • 如何快速升级Java 8 到Java11
  • 内卷把同事逼成了“扫地僧”,把Git上所有面试题整理成足足24W字Java八股文
  • 【计组】主存储器有关知识梳理
  • QT对象树
  • 什么是B+树
  • 【Unity游戏破解】外挂原理分析
  • windows 关闭指定端口进程
  • 虚拟化系列教程:创建 KVM 虚机的几种方式
  • MacBook安装Golang Oracle数据库驱动程序
  • Elasticsearch 核心技术(七):IK 中文分词器的安装、使用、自定义字典
  • 【LeetCode】剑指 Offer(19)
  • 吐血整理,web自动化测试,POM模式搭建自动化测试框架(超级详细)
  • 【数据库原理复习】索引 视图 sql语句
  • 【HDFS】IPC重试
  • Revit导出CAD图纸操作及批量导出
  • 【批处理脚本】-3.4-goto命令详解
  • 超详细CentOS7 NAT模式(无图形化界面即最小安装)网络配置
  • 【可信平台】开证问题汇总--1.无采购入库记录,2.箱码无产出记录
  • RolePred: Open-Vocabulary Argument Role Prediction for Event Extraction 论文解读
  • 【数据结构】链表相关题目(简单版)
  • 通信原理 | FFT/STFT 你真的学会了吗?
  • Qt使用API实现鼠标点击操作
  • JavaWeb学习-Tomcat
  • 【蓝牙系列】蓝牙5.4到底更新了什么(2)
  • js中window自带的四舍五入toFixed方法中的坑以及解决办法
  • JeecgBoot 3.5.0 版本发布,开源的企业级低代码平台