【Spring专题】Spring之Bean的生命周期源码解析——阶段二(二)(IOC之属性填充/依赖注入)

news2024/12/23 23:00:42

目录

  • 前言
    • 阅读准备
    • 阅读指引
    • 阅读建议
  • 课程内容
    • 一、依赖注入方式(前置知识)
      • 1.1 手动注入
      • 1.2 自动注入
        • 1.2.1 XML的autowire自动注入
          • 1.2.1.1 byType:按照类型进行注入
          • 1.2.1.2 byName:按照名称进行注入
          • 1.2.1.3 constructor:按照构造方法进行注入
          • 1.2.1.4 其他
          • 1.2.1.5 XML的autowire自动注入方式总结
        • 1.2.2 @Autowired注解的自动注入
        • 1.2.3 自动注入总结
    • 二、依赖注入过程
      • 2.1 简单回顾
      • 2.2. 概念回顾
      • 2.3 核心方法讲解
    • 三、【寻找注入点】方法讲解
  • 学习总结

前言

阅读准备

由于Spring源码分析是一个前后联系比较强的过程,而且这边分析,也是按照代码顺序讲解的,所以不了解前置知识的情况下,大概率没办法看懂当前的内容。所以,特别推荐看看我前面的文章(自上而下次序):

  • Spring底层核心原理解析【学习难度:★★☆☆☆
  • 手写简易Spring容器过程分析【学习难度:★★☆☆☆
  • Spring之底层架构核心概念解析【学习难度:★★★☆☆,重要程度:★★★★★
  • Bean的生命周期流程图【学习难度:☆☆☆☆☆,重要程度:★★★★★
  • Spring之Bean的生命周期源码解析——阶段一(扫描生成BeanDefinition)【学习难度:★★☆☆☆,重要程度:★★★☆☆
  • Spring之Bean的生命周期源码解析——阶段二(IOC之实例化)【学习难度:★★★★★,重要程度:★★★☆☆

(PS:特别是《Bean的生命周期流程图》,帮大家【开天眼】,先了解下流程。毕竟【通过业务了解代码,远比通过代码了解业务简单的多】!!!!)
(PS:特别是《Bean的生命周期流程图》,帮大家【开天眼】,先了解下流程。毕竟【通过业务了解代码,远比通过代码了解业务简单的多】!!!!)
(PS:特别是《Bean的生命周期流程图》,帮大家【开天眼】,先了解下流程。毕竟【通过业务了解代码,远比通过代码了解业务简单的多】!!!!)

阅读指引

我们在上一节课已经说到过了,本次Spring源码剖析的总入口是new AnnotationConfigApplicationContext(“org.tuling.spring”);,这里就不再重复解释了。本节课要说的内容,是SpringIOC的属性填充/依赖注入,我们这里直接给到入口吧,调用链如下:(调用链比较深,不要纠结细枝末节)

  1. AbstractApplicationContext#refresh:刷新方法,不用在意
  2. AbstractApplicationContext#finishBeanFactoryInitialization:在这里实例化所有剩余的(非lazy-init)单例
  3. DefaultListableBeanFactory#preInstantiateSingletons:在这里实例化所有剩余的(非lazy-init)单例(上面的方法,核心干活的方法就是这里)
  4. DefaultListableBeanFactory#getBean:获取Bean的方法
  5. AbstractBeanFactory#doGetBean:返回指定bean的一个实例,它可以是共享的,也可以是独立的
  6. 上面这个AbstractBeanFactory#doGetBean里面的一段局部代码写的回调方法,如下:
	// 如果是单例创建bean实例
	if (mbd.isSingleton()) {
		sharedInstance = getSingleton(beanName, () -> {
			try {
				return createBean(beanName, mbd, args);
			}
			catch (BeansException ex) {
				// Explicitly remove instance from singleton cache: It might have been put there
				// eagerly by the creation process, to allow for circular reference resolution.
				// Also remove any beans that received a temporary reference to the bean.
				destroySingleton(beanName);
				throw ex;
			}
		});
		beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
	}
  1. AbstractAutowireCapableBeanFactory#createBean:这个类的中心方法:创建一个bean实例,填充bean实例,应用后处理器,等等。
  2. AbstractAutowireCapableBeanFactory#populateBean:使用来自bean定义的属性值在给定的BeanWrapper中填充bean实例。

如上面的调用链所示,最后一个方法,才是我们本次要研究的核心方法。

阅读建议

  1. 看源码,切记纠结细枝末节,不然很容易陷进去。正常来说,看主要流程就好了
  2. 遇到不懂的,多看看类注释或者方法注释。Spring这种优秀源码,注释真的非常到位
  3. 如果你是idea用户,多用F11的书签功能。
    • Ctrl + F11 选中文件 / 文件夹,使用助记符设定 / 取消书签 (必备)
    • Shift + F11 弹出书签显示层 (必备)
    • Ctrl +1,2,3…9 定位到对应数值的书签位置 (必备)

课程内容

一、依赖注入方式(前置知识)

在Spring中,属性注入的方式分为两种,分别是:【手动注入】和【自动注入】。

1.1 手动注入

在XML中定义Bean时,就是手动注入,因为是程序员手动给某个属性指定了值。如下:

<bean name="userService" class="com.luban.service.UserService">
 <property name="orderService" ref="orderService"/>
</bean>

有经验的同学应该知道,上面这种底层是通过setXxx方法进行注入的。另外,还有一种方式,是通过构造方法进行注入的,如下:

<bean name="userService" class="com.luban.service.UserService">
 <constructor-arg index="0" ref="orderService"/>
</bean>

所以手动注入的底层也就是分为两种:【set方法注入】和【构造方法注入】。

1.2 自动注入

自动注入又分为两种:【XML的autowire自动注入】和【@Autowired注解的自动注入】。

1.2.1 XML的autowire自动注入

在XML中,我们可以在定义一个Bean时去指定这个Bean的自动注入模式,它有如下几种方式:

1.2.1.1 byType:按照类型进行注入

byType注入方式,底层是基于setXxx方法实现的,所以setter方法不能少。这里说的类型是【入参】的类型。
Spring在通过byType的自动填充属性时流程是:

  1. 获取到set方法中的唯一参数的参数类型,并且根据该类型去容器中获取bean
  2. 如果找到多个,会报错

使用示例如下:

<bean id="userService" class="com.luban.service.UserService" autowire="byType"/>
    public void setOrderService(OrderService orderService) {
        this.orderService = orderService;
    }

如上示例的类型,就是指入参orderService的类型OrderService

1.2.1.2 byName:按照名称进行注入

byType注入方式,底层是基于setXxx方法实现的,所以setter方法不能少。这里说的【名称】,是指setXxx后面的Xxx部分。
所以,Spring在通过byName的自动填充属性时流程是:

  1. 找到所有set方法所对应的Xxx部分的名字
  2. 根据Xxx部分的名字去获取bean

使用示例如下:

    <bean id="userXmlBean" class="org.tuling.spring.xml.bean.UserXmlBean" autowire="byName"/>
    <bean id="walletXmlBean" class="org.tuling.spring.xml.bean.WalletXmlBean"/>

如上,我们定义了userXmlBean的自动注入类型是byName,并且定义了一个名字叫walletXmlBean的bean。

public class UserXmlBean {

    private WalletXmlBean wallet;

    public void printProperty() {
        System.out.println(wallet);
    }

    public void setWalletXmlBean(WalletXmlBean param) {
        this.wallet = param;
    }
}

如上,我们定义了一个UserXmlBean ,他有成员变量WalletXmlBean wallet。同时给他声明了一个成员方法printProperty()用来打印它的成员属性的地址。

测试代码:

public class MyXmlApplicationContextTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        UserXmlBean userXmlBean = (UserXmlBean)context.getBean("userXmlBean");
        userXmlBean.printProperty();
    }
    
    // 系统输出:
    // org.tuling.spring.xml.bean.WalletXmlBean@1d16f93d
}

如上,UserXmlBeanWalletXmlBean类型的属性出现了2个名称,一个是成员变量wallet,另一个是setter方法入参中的param,但是一点都不妨碍我们byName注入。因为,根据byName的规则,寻找的是setXxx后面的Xxx部分。还不够信服是吗?我们改一下UserXmlBean里面的setter方法,如下:

    public void setWalletXmlBean123(WalletXmlBean param) {
        this.wallet = param;
    }

这个时候再去调用,输出null

1.2.1.3 constructor:按照构造方法进行注入

constructor表示通过构造方法注入,其实这种情况就比较简单了,没有byType和byName那么复杂。
如果是constructor,那么就可以不写set方法了,当某个bean是通过构造方法来注入时,spring利用构造方法的参数信息从Spring容器中去找bean,找到bean之后作为参数传给构造方法,从而实例化得到一个bean对象,并完成属性赋值(属性赋值的代码得程序员来写)。
(PS:我们这里先不考虑一个类有多个构造方法的情况,后面单独讲推断构造方法。我们这里只考虑只有一个有参构造方法。)
其实构造方法注入相当于byType+byName。Spring在通过byName的自动填充属性时流程是:

  1. 通过构造方法中的参数类型去找bean,如果对应的类型只有一个bean,那就是它了;
  2. 如果找到多个会根据参数名确定
  3. 如果最后根据参数名都无法确定,则报错

使用示例如下:

    <bean id="userXmlBean" class="org.tuling.spring.xml.bean.UserXmlBean" autowire="constructor"/>
    <bean id="walletXmlBean123" class="org.tuling.spring.xml.bean.WalletXmlBean"/>
    <bean id="walletXmlBean" class="org.tuling.spring.xml.bean.WalletXmlBean"/>

bean示例:

public class UserXmlBean {

    private WalletXmlBean wallet;

    public void printProperty() {
        System.out.println(wallet);
    }
    
    public UserXmlBean(WalletXmlBean walletXmlBean) {
        this.wallet = walletXmlBean;
    }
}

具体的调用跟错误方式这边就不介绍了,大家回头自己试试吧

1.2.1.4 其他

其他,诸如:

  • default:表示默认值,我们一直演示的某个bean的autowire,而也可以直接在<beans>标签中设置autowire,如果设置了,那么<bean>标签中设置的autowire如果为default,那么则会用<beans>标签中设置的autowire
  • no:表示关闭autowire,不自动注入
1.2.1.5 XML的autowire自动注入方式总结

那么XML的自动注入底层其实也就是:

  1. set方法注入
  2. 构造方法注入

1.2.2 @Autowired注解的自动注入

@Autowired注解,本质上也是byType和byName的结合。它是先byType,如果找到多个则byName。这个跟xml构造方式注入原理如出一辙。就是:

  1. 先根据类型去找bean,如果对应的类型只有一个bean,那就是它了;
  2. 如果找到多个会根据属性名确定
  3. 如果最后根据属性名都无法确定,则报错

@Autowired注解可以写在:

  1. 属性上:先根据属性类型去找Bean,如果找到多个再根据属性名确定一个(属性注入)
  2. 构造方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个(构造方法注入)
  3. set方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个(set方法注入)

1.2.3 自动注入总结

可以发现XML中的自动注入是挺强大的,那么问题来了,为什么我们平时都是用的@Autowired注解呢?而没有用上文说的这种自动注入方式呢?
其实啊,@Autowired注解相当于XML中的autowire属性的注解方式的替代。从本质上讲,@Autowired注解提供了与autowire相同的功能,但是拥有更细粒度的控制和更广泛的适用性。
XML中的autowire控制的是整个bean的所有属性,而@Autowired注解是直接写在某个属性、某个set方法、某个构造方法上的。
再举个例子,如果一个类有多个构造方法,那么如果用XML的autowire=constructor,你无法控制到底用哪个构造方法,而你可以用@Autowired注解来直接指定你想用哪个构造方法。
同时,用@Autowired注解,还可以控制,哪些属性想被自动注入,哪些属性不想,这也是细粒度的控制。

二、依赖注入过程

2.1 简单回顾

依赖注入的过程,大体上其实能分为以下三步的:【寻找注入点】、【填充属性】、【填充属性后】。但其实,【寻找注入点】这个过程,会在两个地方被调用。第一个就是箭头所向,【实例化】阶段【BeanDefinition后置处理】那个地方。怎么理解呢?因为,【寻找注入点】的实现类就是【BeanDefinition后置处理】中的一个
在这里插入图片描述
上面说的概念多少有点绕。简单来说,【寻找注入点】就是寻找被@Autowird@Value@Inject@Resource注解修饰的属性、方法等等。

2.2. 概念回顾

在这个【实例化】过程中,涉及到了一些Spring底层设计的概念,我在上一个笔记里面有大概介绍过Spring底层概念的一些讲解,不记得的同学记得回去翻一翻。
主要涉及的概念有:

  • BeanDefinition(设计图纸):BeanDefinition表示Bean定义,BeanDefinition中存在很多属性用来描述一个Bean的特征
  • MergedBeanDefinitionPostProcessor:合并BeanDefinition后置处理器。但这里其实主要说的是AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor。他俩有什么作用呢?前者是处理Spring内部定义的@Autowired@Value自动注入注解;后者是处理jdk定义的@Resource注解。既然都说到这了,那你们猜猜用到的是什么方法?嘿,不就是MergedBeanDefinitionPostProcessor里面定义的方法嘛,赶紧去翻翻看。

CommonAnnotationBeanPostProcessor接口定义如下:

 /**
   * 这个后置处理器通过继承InitDestroyAnnotationBeanPostProcessor和InstantiationAwareBeanPostProcessor注解,
   * 获得了对@PostConstruct和@PreDestroy的支持。
   * 另外,这个类的核心处理元素是@Resource注解
   */
public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor
		implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
	// 具体代码就不贴了。学到这里大家应该知道如何通过【接口】继承、实现来猜测类能力了吧
}

AutowiredAnnotationBeanPostProcessor接口定义如下:

// 继承类跟CommonAnnotationBeanPostProcessor 如出一辙,唯一不同的是,继承了功能更强大的
// SmartInstantiationAwareBeanPostProcessor(InstantiationAwareBeanPostProcessor子类)
// 实现这个类,是为了实现里面的推断构造方法
public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
		MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {

2.3 核心方法讲解

本节【属性注入】,将会分两个部分来讲。第一部分是:【寻找注入点】;剩下的是第二部分。
先说第一部分,第一部分主要涉及【3个类,7个核心方法】。
第二部分,待定…

三、【寻找注入点】方法讲解

学习总结

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

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

相关文章

快速实现SAP的移动化和流程优化

热门议题&#xff1a; 1、企业如何快速解决人员移动办公的需求&#xff0c;比如在苹果安卓手机&#xff0c;平板电脑&#xff0c;MAC登录SAP。2、企业如何解决用户经常抱怨的流程复杂&#xff0c;操作繁琐&#xff0c;难以使用等问题 公司介绍&#xff1a; Synactive,Inc. 是…

【校招VIP】前端校招考点之vue底层特性

考点介绍&#xff1a; 大家在面试途中遇到的相对多的问题&#xff0c;也是难点的问题&#xff0c;一般都有vue底层原理。对于只会用但是不懂的小白来说真是太痛苦了&#xff0c;仅仅能说出来 一些 数据劫持&#xff0c;双向数据绑定&#xff0c;虚拟dom树的名词来说远远不够。 …

通过 HttpClient 发送请求

文章目录 1. 引入 maven 依赖2. 发送 GET 方式的请求3. 发送 POST 方式的请求 1. 引入 maven 依赖 <dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId> </dependency>2. 发送 GET 方式的请求…

《Go 语言第一课》课程学习笔记(六)

变量声明&#xff1a;静态语言有别于动态语言的重要特征 变量所绑定的内存区域是要有一个明确的边界的。也就是说&#xff0c;通过这样一个变量&#xff0c;我们究竟可以操作 4 个字节内存还是 8 个字节内存&#xff0c;又或是 256 个字节内存&#xff0c;编程语言的编译器或解…

【考研数学】概率论与数理统计 | 第一章——随机事件与概率(3,全概率公式、贝叶斯公式与三大概型)

文章目录 引言六、全概率公式与贝叶斯公式6.1 全概率公式6.2 贝叶斯公式 七、三大概型7.1 古典概型7.2 几何概型7.3 伯努利概型 引言 承接前文&#xff0c;在事件的独立之后&#xff0c;我们开始学习全概率公式、贝叶斯公式以及概型。 六、全概率公式与贝叶斯公式 定义—— 完…

【Diffusion】李宏毅2023机器学习Diffusion笔记

文章目录 1 想法概述2 实际过程阶段1 Add Noise阶段2 Denoise 3 数学原理4 为什么推理时要额外加入noise5 一些不知道对不对的Summary 1 想法概述 从一张充满噪声的图中不断denoise&#xff0c;最终得到一张clear的图片。为了确定当前图片中噪声占比的大小&#xff0c;同时输入…

【福建事业单位-公共基础-哲学】01哲学基本概述、唯物论和唯物辩证法(发展联系)

【福建事业单位-公共基础-】01哲学基本概述和唯物论 一、哲学1.1 哲学的概念1.2 哲学的基本问题—思维和存在的关系问题/意识和物质1.3哲学的基本派别:唯物主义与唯心主义古代朴素唯物主义近代形而上学唯物主义辩证唯物主义和历史唯物主义主观和客观唯心主义 1.4辩证法与形而上…

LVS负载均衡集群-NAT模式部署

集群 集群&#xff1a;将多台主机作为一个整体&#xff0c;然后对外提供相同的服务 集群使用场景&#xff1a;高并发的场景 集群的分类 1.负载均衡器集群 减少响应延迟&#xff0c;提高并发处理的能力 2&#xff0c;高可用集群 增强系统的稳定性可靠性&…

C++ 网络编程项目fastDFS分布式文件系统(三)-Nginx部分

目录 1. 一些基本概念 1.1 Nginx初步认识 1.2 正向/反向代理 1.3 域名和IP 2. Nginx 安装和配置 2.1 安装 2.2 配置 3. Nginx的使用 3.1 部署静态网页 3.2 反向代理和负载均衡 4 课外知识导读 1. URL和URI ​编辑 2. DNS解析过程 1. 一些基本概念 1.1 Nginx初步认…

如何在控制台查看excel内容

背景 最近发现打开电脑的excel很慢&#xff0c;而且使用到的场景很少&#xff0c;也因为mac自带了预览的功能。但是shigen就是闲不住&#xff0c;想自己搞一个excel预览软件&#xff0c;于是在一番技术选型之后&#xff0c;我决定使用python在控制台显示excel的内容。 具体的需…

Java进阶篇--数据结构

目录 一.数组&#xff08;Array&#xff09;&#xff1a; 1.1 特点&#xff1a; 1.2 基本操作&#xff1a; 1.3 使用数组的好处包括&#xff1a; 1.4 数组也有一些限制&#xff1a; 二.集合框架&#xff08;Collections Framework&#xff09;&#xff1a; 2.1 列表…

wsl安装Linux kali

目录 1.启用“Hyper-V”和“~子系统” 2.启用虚拟化 3.安装发行版 4.升级原有系统到WSL2 5.kali换源与更新升级并安装工具集 6.kali安装图形界面~GUI 7.kali安装中文界面与中文输入法 8.wsl~kali位置迁移 1.启用“Hyper-V”和“~子系统” 打开控制面板---->>程序…

JupyterHub实战应用

一、JupyerHub jupyter notebook 是一个非常有用的工具&#xff0c;我们可以在浏览器中任意编辑调试我们的python代码&#xff0c;并且支持markdown 语法&#xff0c;可以说是科研利器。但是这种情况适合个人使用&#xff0c;也就是jupyter notebook以我们自己的主机作为服务器…

SpringBoot-lombok

为什么要使用lombok? Lombok是一个通过注解以达到减少代码的Java库,如通过注解的方式减少getter,setter方法,构造方法等。通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法&#xff0c;并可以自动化生成日志变量&#xff0c;简化java开发、提高…

spring boot 整合mongodb

1、安装依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency>2、配置数据库连接 spring:data:mongodb:host: localhostport: 27017username: xxxxxxp…

vue:this和that的理解

当我们进入公司的时候会发现一个很常见的情况&#xff0c;就是你的前开发者会常用这么一个变量&#xff1a;that、self… 为什么会用到that、self呢&#xff0c;小编是这么理解的&#xff0c;this指向的是当前的对象&#xff0c;而that、self是临时的变量&#xff0c;为了临时存…

【STM32+ESP8266上云连载①】给ESP8266烧录AT固件

文章目录 一、给NodeMCU烧录固件1.1硬件准备1.2软件准备1.3AT固件下载1.4配置设置1.5开始烧录 二、给ESP8266-01S烧录固件2.1硬件准备2.2AT固件下载2.3连线2.4烧录配置 三、给ESP-12E/F/S单片烧录固件四、指令测试4.1HTTP测试4.2MQTT测试 我在使用ESP8266的时候遇到了一些问题&…

leetcode中常用的 C++函数和 STL容器

C函数和 STL容器 C排序sortC翻转字符串reverseC截取字符串strsub哈希表的使用定义查询哈希表里是否有该key在哈希表里存放键值链式哈希 哈希集合定义一个哈希集合查找一个字符是否在set里面删除和添加 优先队列 最大堆优先队列的大顶堆定义方式插入存储数组对定义插入 双端队列…

收集的一些比较好的git网址

1、民间故事 https://github.com/folkstory/lingqiu/blob/master/%E4%BC%A0%E8%AF%B4%E9%83%A8%E5%88%86/%E4%BA%BA%E7%89%A9%E4%BC%A0%E8%AF%B4/%E2%80%9C%E6%B5%B7%E5%BA%95%E6%8D%9E%E6%9C%88%E2%80%9D%E7%9A%84%E6%AD%A6%E4%B8%BE.md 2、童话故事 https://gutenberg.org/c…

微信支付报非法的密钥大小: Caused by: java.security.InvalidKeyException: Illegal key size

在Linux环境中出现 java.security.InvalidKeyException: Illegal key size 异常通常是由于Java默认的加密限制引起的。Java默认的加密强度限制了加密算法密钥的最大长度 方式一 1. 找到该目录 /usr/java/jdk1.8.0_121/jre/lib/security 2. 替换local_policy.jar 和 US_export_…