Spring源码(三)Spring Bean生命周期

news2024/9/23 11:27:03

Bean的生命周期就是指:在Spring中,一个Bean是如何生成的,如何销毁的

Bean生命周期流程图

在这里插入图片描述

1、生成BeanDefinition

Spring启动的时候会进行扫描,会先调用org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents(String basePackage)
扫描某个包路径,并得到BeanDefinition的Set集合。

  1. 首先,通过ResourcePatternResolver获得指定包路径下的所有.class文件(Spring源码中将此文件包装成了Resource对象)
  2. 遍历每个Resource对象
  3. 利用MetadataReaderFactory解析Resource对象得到MetadataReader(在Spring源码中MetadataReaderFactory具体的实现类为CachingMetadataReaderFactory,MetadataReader的具体实现类为SimpleMetadataReader)
  4. 利用MetadataReader进行excludeFilters和includeFilters,以及条件注解@Conditional的筛选(条件注解并不能理解:某个类上是否存在@Conditional注解,如果存在则调用注解中所指定的类的match方法进行匹配,匹配成功则通过筛选,匹配失败则pass掉。)
  5. 筛选通过后,基于metadataReader生成ScannedGenericBeanDefinition
  6. 再基于metadataReader判断是不是对应的类是不是接口或抽象类
  7. 如果筛选通过,那么就表示扫描到了一个Bean,将ScannedGenericBeanDefinition加入结果集

MetadataReader表示类的元数据读取器,主要包含了一个AnnotationMetadata

2、合并BeanDefinition

通过扫描得到所有BeanDefinition之后,就可以根据BeanDefinition创建Bean对象了。但是在Spring中支持父子BeanDefinition,父子BeanDefinition实际用的比较少,使用是这样的

这么定义的情况下,child是单例Bean

<bean id="parent" class="com.wang.service.Parent" scope="prototype"/>
<bean id="child" class="com.wang.service.Child"/>

但是这么定义的情况下,child就是原型Bean了。因为child的父BeanDefinition是parent,所以会继承parent上所定义的scope属性。

<bean id="parent" class="com.wang.service.Parent" scope="prototype"/>
<bean id="child" class="com.wang.service.Child" parent="parent"/>

而在根据child来生成Bean对象之前,需要进行BeanDefinition的合并,得到完整的child的BeanDefinition。

3、加载类

BeanDefinition合并之后,就可以去创建Bean对象了。创建Bean就必须实例化对象,而实例化就必须先加载当前BeanDefinition所对应的class,在AbstractAutowireCapableBeanFactory类的createBean()方法中,一开始就会调用。

if (mbd.hasBeanClass()) {
	return mbd.getBeanClass();
}
if (System.getSecurityManager() != null) {
	return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>) () ->
		doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
	}
else {
	return doResolveBeanClass(mbd, typesToMatch);
}

如果beanClass属性的类型是Class,那么就直接返回,如果不是,则会根据类名进行加载(doResolveBeanClass方法所做的事情)

4、实例化前

BeanDefinition对应的类成功加载后,就可以实例化对象了。但是实例化对象之前,Spring提供了一个扩展点,允许用户来控制是否在某个或某些Bean实例化之前做一些启动动作。这个扩展点叫InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()。比如:

@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		if ("userService".equals(beanName)) {
			System.out.println("实例化前");
		}
		return null;
	}
}

值得注意的是,postProcessBeforeInstantiation()是有返回值的,如果这么实现:

userService这个Bean,在实例化前会直接返回一个由我们所定义的UserService对象。如果是这样,表示不需要Spring来实例化了,并且后续的Spring依赖注入也不会进行了,会跳过一些步骤,直接执行初始化后这一步。

@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		if ("userService".equals(beanName)) {
			System.out.println("实例化前");
			return new UserService();
		}
		return null;
	}
}

5、实例化

在这个步骤中就会根据BeanDefinition去创建一个对象了。

5.1 Supplier创建对象

首先判断BeanDefinition中是否设置了Supplier,如果设置了则调用Supplier的get()得到对象。

得直接使用BeanDefinition对象来设置Supplier,比如:

AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setInstanceSupplier(new Supplier<Object>() {
	@Override
	public Object get() {
		return new UserService();
	}
});
context.registerBeanDefinition("userService", beanDefinition);

5.2 工厂方法创建对象

如果没有设置Supplier,则检查BeanDefinition中是否设置了factoryMethod

<bean id="userService" class="com.wang.service.UserService" factory-method="createUserService" />

对应的UserService类为:

public class UserService {

	public static UserService createUserService() {
		System.out.println("执行createUserService()");
		UserService userService = new UserService();
		return userService;
	}

	public void test() {
		System.out.println("test");
	}

}

5.3 推断构造方法

前面有讲到 Spring源码(二)Spring底层架构核心概念解析

额外的,在推断构造方法逻辑中除开会去选择构造方法以及查找入参对象意外,会还判断是否在对应的类中是否存在使用@Lookup注解了方法。如果存在则把该方法封装为LookupOverride对象并添加到BeanDefinition中。

在实例化时,如果判断出来当前BeanDefinition中没有LookupOverride,那就直接用构造方法反射得到一个实例对象。如果存在LookupOverride对象,也就是类中存在@Lookup注解了的方法,那就会生成一个代理对象。

6、BeanDefinition的后置处理

Bean对象实例化出来之后,接下来就应该给对象的属性赋值了。在真正给属性赋值之前,Spring又提供了一个扩展点MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition(),可以对此时的BeanDefinition进行加工,比如:

@Component
public class MyMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor {

	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		if ("userService".equals(beanName)) {
			beanDefinition.getPropertyValues().add("orderService", new OrderService());
		}
	}
}

7、实例化后

在处理完BeanDefinition后,Spring又设计了一个扩展点:InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(),比如:

@Component
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

	@Override
	public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {

		if ("userService".equals(beanName)) {
			UserService userService = (UserService) bean;
			userService.test();
		}

		return true;
	}
}

8.、自动注入

Spring的自动注入

9、处理属性

这个步骤中,就会处理@Autowired、@Resource、@Value等注解,也是通过InstantiationAwareBeanPostProcessor.postProcessProperties()扩展点来实现的,我们可以实现一个自己的自动注入功能,比如:

@Component
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

	@Override
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
		if ("userService".equals(beanName)) {
			for (Field field : bean.getClass().getFields()) {
				if (field.isAnnotationPresent(ZhouyuInject.class)) {
					field.setAccessible(true);
					try {
						field.set(bean, "123");
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
				}
			}
		}

		return pvs;
	}
}

10、执行Aware

完成了属性赋值之后,Spring会执行一些回调,包括:

  1. BeanNameAware:回传beanName给bean对象。
  2. BeanClassLoaderAware:回传classLoader给bean对象。
  3. BeanFactoryAware:回传beanFactory给对象。

11、初始化前

Spring提供的一个扩展点:BeanPostProcessor.postProcessBeforeInitialization()

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if ("userService".equals(beanName)) {
			System.out.println("初始化前");
		}

		return bean;
	}
}

利用初始化前,可以对进行了依赖注入的Bean进行处理

12、初始化

  1. 查看当前Bean对象是否实现了InitializingBean接口,如果实现了就
  2. 调用其afterPropertiesSet()方法
  3. 执行BeanDefinition中指定的初始化方法

13、初始化后

Spring提供的一个扩展点:BeanPostProcessor.postProcessAfterInitialization()

可以在这个步骤中,对Bean最终进行处理,Spring中的AOP就是基于初始化后实现的,初始化后返回的对象才是最终的Bean对象。

@Component
public class ZhouyuBeanPostProcessor implements BeanPostProcessor {

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if ("userService".equals(beanName)) {
			System.out.println("初始化后");
		}

		return bean;
	}
}

14、Bean销毁

Bean销毁是发送在Spring容器关闭过程中的。

可以直接在方法上加上 @PreDestroy 注解,或者实现DisposableBean 接口

只有单例bean 才会执行destroy方法,原型bean不会。原型bean每次调用getBean时候, 返回的都是新对象

@Component
public class User implements DisposableBean {
    @Override
    public void destroy() throws Exception {
        System.out.println("xxxx");
    }
}

总结一下,主要无非四个阶段:

实例化(Instantiation)
属性赋值(Populate)
初始化(Initialization)
销毁(Destruction)

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

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

相关文章

【C++入门到精通】C++入门 —— 类和对象(了解类和对象)

目录 一、类和对象的历史由来 二、面向过程和面向对象的初步认识 三、类 1.引子 2.类的定义 3.类的访问限定符及封装 ⭕访问限定符 &#x1f6a9;访问限定符解释说明 &#x1f6a9;struct 与 class 的区别 1. 默认访问级别&#xff1a; 2. 继承权限&#xff08;默认的…

安全基础 --- html标签 + 编码

html标签 &#xff08;1&#xff09;detail标签 <details>标签用来折叠内容&#xff0c;浏览器会折叠显示该标签的内容。 <1> 含义&#xff1a; <details> 这是一段解释文本。 </details> 用户点击这段文本&#xff0c;折叠的文本就会展开&#x…

山西电力市场日前价格预测【2023-07-31】

日前价格预测 预测明日&#xff08;2023-07-31&#xff09;山西电力市场全天平均日前电价为294.49元/MWh。其中&#xff0c;最高日前电价为318.11元/MWh&#xff0c;预计出现在19: 30。最低日前电价为278.89元/MWh&#xff0c;预计出现在00: 15。 价差方向预测 1&#xff1a;实…

Git远程仓库的创建、克隆、推送和拉取

文章目录 1.前言2.远程仓库的创建3.远程仓库的克隆3.1 使用HTTPS进行克隆3.2 使用SSH进行克隆 4.远程仓库的推送5. 远程仓库的拉取 1.前言 在之前的文章中,讲解了Git的一些基本概念和常用的命令. 是时候干一件大事了-实现多人协助开发! 环境:Centos7云服务器 代码托管平台: G…

基于LSTM神经网络的BIM对象识别【BIM+AI】

BIM 模型中的一个基本数据是对象的名称&#xff0c;尤其是房间。 没有专有名称&#xff0c;人们就不可能理解模型/设计的内容。 在本文中&#xff0c;我们尝试使用 Tensorflow 构建一个基于该数据识别房间的LSTM神经网络模型。 推荐&#xff1a;用 NSDT设计器 快速搭建可编程3D…

第一章HelloWorld

确认环境 java -version javac编写代码 public class HelloWorld{public static void main(String[] args){System.out.println("HelloWorld");} }编译运行 javac HelloWord.java编译java文件成class字节码文件 java HelloWorld运行.class字节码文件 备注 容易…

手把手教你使用stable diffusion生成自己的艺术二维码

艺术二维码制作指南 导读midjourneystable diffusion 环境准备安装stable diffusion webuisd-webui-qrcode-toolkit安装 草料二维码模型准备QR PatternQR Code MonsterIoC Lab Control Net 艺术二维码制作1. 二维码信息提取2. 使用QR Tookit生成二维码3. 下载二维码图片4. prom…

电脑维护指南:让你的战友始终高效稳定

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

管理类联考——写作——论说文——实战篇——立意篇——真题审题立意汇总分类

难度&#xff1a;*号越多越难&#xff0c;越需要看。 角度3——4种材料类型、4个立意对象、5种写作态度 寓言类材料**** 2022年 鸟类会飞是因为它们在进化中不断优化了其身体结构。飞行是一项较为特殊的运动&#xff0c;鸟类的躯干进化出了适合飞行的流线型。飞行也是一项需…

【数理知识】刚体基本运动

文章目录 1 刚体定义2 自由刚体3 两种基本运动1 平动2 转动 4 举例 11 计算质心位置&#xff0c;求差得到平移向量2 计算协方差矩阵3 奇异值分解4 计算旋转矩阵 Ref 1 刚体定义 刚体就是质点间距离保持不变的质点系。 刚体的空间位置由任意与刚体固连的不共线三点决定。 2 自…

HDMI协议实现彩条静态显示方块移动

文章目录 前言一、硬件原理1、硬件设计原理图2、引脚图 二、系统设计1、系统模块框图2、RTL视图3、RGB2DVI 模块框图4、ALTDDIO_OUT IP 核的信号框图 三、HDMI的行&场时序1、时序图2、常见显示器时序参数 四、源码1、video_driver(显示驱动模块)2、serializer_10_to_1(并转…

微信公众号接入AI ChatGPT机器人bot的详细教程

微信公众号可以与客服系统进行对接&#xff0c;实现智能自动回复或者人工回复的公众号客服系统实现对接的前提是需要公众号为认证的服务号&#xff0c;实现的功能概况&#xff1a;公众号客服接口对接&#xff0c;公众号模板消息提醒&#xff0c;网页授权获取到微信的昵称头像&a…

Python实现GA遗传算法优化支持向量机回归模型(SVR算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 遗传算法&#xff08;Genetic Algorithm&#xff0c;GA&#xff09;最早是由美国的 John holland于20世…

tinkerCAD案例:24. Ruler - Measuring Lengths 标尺 -量勺

tinkerCAD案例&#xff1a;24. Ruler - Measuring Lengths 标尺 - 测量长度 Project Overview: 项目概况&#xff1a; A machine shop, where any idea can become a reality, can cost millions and million of dollars. Still, the most important tool in the shop is the…

基于Open3D的点云处理12-体素化

体素化Voxelization 体素&#xff08;voxel&#xff09;是像素&#xff08;pixel&#xff09;、体积&#xff08;volume&#xff09;和元素&#xff08;element&#xff09;的组合词&#xff0c;相当于3D空间中的像素; 体素化是通过用空间均匀大小的体素网格(voxel grid)来模…

openpnp - ReferenceStripFeeder 改版零件

文章目录 openpnp - ReferenceStripFeeder 改版零件概述笔记整体效果散料飞达主体磁铁仓盖板飞达编带中间压条飞达编带两边压条装配体用的8mm编带模型END openpnp - ReferenceStripFeeder 改版零件 概述 官方推荐了ReferenceStripFeeder的模型smd_strip_feeders_mod_tray.zip…

【C++学习】STL容器——vector

目录 一、vector的介绍及使用 1.1 vector的介绍 1.2 vector的使用 1.2.1 vector的定义 1.2.2 vector iterator 的使用 1.2.3 vector 空间增长问题 1.2.4 vector 增删查改 1.2.5 vector 迭代器失效问题&#xff08;重点&#xff09; 二、vector深度剖析及模拟实现 ​编辑…

【JavaSE】数组的定义与使用

【本节目标】 1. 理解数组基本概念 2. 掌握数组的基本用法 3. 数组与方法互操作 4. 熟练掌握数组相关的常见问题和代码 目录 1. 数组的基本概念 1.1什么是数组 1.2 数组的创建及初始化 1.3 数组的使用 2. 数组是引用类型 2.1基本类型变量与引用类型变量的区别 2.2再谈引用…

慎思笃行,兴业致远:金融行业的数据之道

《中庸》中说&#xff0c;“博学之&#xff0c;审问之&#xff0c;慎思之&#xff0c;明辨之&#xff0c;笃行之”。这段话穿越千年&#xff0c;指引着中国千行百业的发展。对于金融行业来说&#xff0c;庞大的数据量可以说是“博学”的来源。但庞大的数据体量&#xff0c;既是…

网络通讯(服务端搭建)

一.本篇概况 本篇文章主要以C语言为主&#xff0c;通过C语言中所设定的函数以及环境来将网络通讯的服务端进行搭建。注&#xff1a;本篇并未涉及服务端与客户端之间的收发数据。 二.代码实现 1.初始化套接字库&#xff1a; if(WSAStartup(MAKEWORD(2, 2), &wsaData) ! 0…