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

Spring中Bean的生命周期详解

Spring中Bean的生命周期详解

Spring最重要的功能就是帮助程序员创建对象(也就是IOC),而启动Spring就是为创建Bean对象做准备,所以我们先明白Spring到底是怎么去创建Bean的,也就是先弄明白Bean的生命周期。

ApplicationContext中的属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K1e3bZGS-1664138010637)(Spring中Bean的生命周期详解.assets/image-20211029103246697.png)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qrLI32Vu-1664138010639)(D:\atguigu\知识点笔记(教学)\bean生命周期\1.png)]

Bean的生成过程

1. 生成BeanDefinition

Spring启动的时候会进行扫描,会先调用

Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

拿到所指定的包路径下的所有文件资源(******.class文件)

然后会遍历每个Resource,为每个Resource生成一个MetadataReader对象,这个对象拥有三个功能:

  1. 获取对应的Resource资源

  2. 获取Resource对应的class的元数据信息,包括类的名字、是不是接口、是不是一个注解、是不是抽象类、有没有父类,父类的名字,所实现的所有接口的名字,内部类的类名等等。

  3. 获取Resource对应的class上的注解信息,当前类上有哪些注解,当前类中有哪些方法上有注解

在生成MetadataReader对象时,会利用ASM技术解析class文件,得到类的元数据集信息合注解信息,在这个过程中也会利用ClassLoader去加载注解类(ClassUtils.getDefaultClassLoader()所获得的类加载器),但是不会加载本类。

有了MetadataReader对象,就相当于有了当前类的所有信息,但是当前类并没有加载,也是可以理解的,真正在用到这个类的时候才加载。

然后利用MetadataReader对象生成一个ScannedGenericBeanDefinition对象,注意此时的BeanDefinition对象中的beanClass属性存储的是当前类的名字,而不是class对象。(beanClass属性的类型是Object,它即可以存储类的名字,也可以存储class对象)

最后,通过扫描得到BeanDefinition对象,我们还可以通过直接定义BeanDefinition,或 解析spring.xml文件的,或者@Bean注解得到BeanDefinition对象

2. 合并BeanDefinition

通过扫描得到所有BeanDefinition之后,就可以根据BeanDefinition创建Bean对象了,但是在 Spring中支持父子BeanDefinition,和Java父子类类似,但是完全不是一回事。

父子BeanDefinition实际用的比较少,使用是这样的,比如:

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

是这么定义的情况下,child就是原型Bean了。

如果某个BeanDefinition存在父BeanDefinition,那么则要进行合并

3. 加载类

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

Class<?> resolvedClass = resolveBeanClass(mbd, beanName);

这行代码就是去加载类,该方法是这么实现的:

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

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

会利用BeanFactory所设置的类加载器来加载类,如果没有设置,则默认使用**ClassUtils.getDefaultClassLoader()**所返回的类加载器来加载。

ClassUtils.getDefaultClassLoader()

  1. 优先获取当前线程中的ClassLoader

  2. 如果为空,则获取加载ClassUtils类的类加载器(正常情况下,就是AppClassLoader,但是如果是在Tomcat中运行,那么则会是Tomcat中为每个应用所创建的WebappClassLoader)

  3. 如果为空,那么则是bootstrap类加载器加载的ClassUtils类,那则获取系统类加载器进行加载

4. 实例化前

允许第三方可以不按照Spring的正常流程来创建一个Bean,可以利用InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation方法来提前返回一个Bean对象,直接结束Bean的生命周期

5. 实例化

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

5.1 Supplier创建对象

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

5.2 工厂方法创建对象

如果没有设置Supplier,则检查BeanDefinition中是否设置了factoryMethod,也就是工厂方法,有 两种方式可以设置factoryMethod,比如:

方式一:

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

对应的userServices为:

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

方式二:

<bean id="commonService" class="com.hzk.service.CommonService"/>
<bean id="userService1" factory‐bean="commonService" factory‐method="createUserService"
/>

对应的CommonService的类为:、

public class CommonService {
public UserService createUserService() {
return new UserService();
}
}

Spring发现当前BeanDefinition方法设置了工厂方法后,就会区分这两种方式,然后调用工厂方法得 到对象。

tips:

我们通过@Bean所定义的BeanDefinition,是存在factoryMethod和factoryBean 的,也就是和上面的方式二非常类似,@Bean所注解的方法就是factoryMethod,AppConfig对象 就是factoryBean。如果@Bean所所注解的方法是static的,那么对应的就是方式一。

5.3 推断构造方法

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

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

@Component
public class UserService {
private OrderService orderService;
public void test() {
OrderService orderService = createOrderService();
System.out.println(orderService);
}
@Lookup("orderService")
public OrderService createOrderService() {
return null;
}
}

构造方法反射得到一个实例

6. BeanDefinition的后置处理

for (BeanPostProcessor bp : getBeanPostProcessors()) {
	if (bp instanceof MergedBeanDefinitionPostProcessor) {
		MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
		bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
	}
}

这里可以处理BeanDefinition,但是此时实例对象已经生成好了,所以修改beanClass已经没用了,但是可以修改PropertyValues,比如:

@Component
public class LubanMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor {

	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		if (beanName.equals("userService")) {
			beanDefinition.setBeanClass(User.class); // 没用
			beanDefinition.getPropertyValues().add("name","xxx");
		}
	}
}

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. 填充属性

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

9. 执行Aware

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

包括: 1. BeanNameAware:回传beanName给bean对象。

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

10. 初始化前

初始化前,也是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进行处理。

在Spring源码中:

  1. InitDestroyAnnotationBeanPostProcessor会在初始化前这个步骤中执行@PostConstruct的 方法,

  2. ApplicationContextAwareProcessor会在初始化前这个步骤中进行其他Aware的回调:

    i. EnvironmentAware:回传环境变量

    ii. EmbeddedValueResolverAware:回传占位符解析器 iii. ResourceLoaderAware:回传资源加载器

    iv. ApplicationEventPublisherAware:回传事件发布器

    v. MessageSourceAware:回传国际化资源

    vi. ApplicationStartupAware:回传应用其他监听对象,可忽略

    vii. ApplicationContextAware:回传Spring容器ApplicationContex

11. 初始化

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

12. 初始化后

这是Bean创建生命周期中的最后一个步骤,也是Spring提供的一个扩展点: BeanPostProcessor.postProcessAfterInitialization(),比如:

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

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

BeanPostProcessor(Bean的后置处理器)

Bean的后置处理器是指Spring在创建一个Bean的过程中,可以通过后置处理器来干涉Bean的创建过程

一个简单的Bean的生命周期:

  1. 推断构造方法(确定使用哪个构造方法来实例化对象)

  2. 实例化

  3. 填充属性

  4. 初始化

Spring在这个基础上,在这4步中的某些"间隙"中增加了扩展点,比如:

  1. BeanPostProcessor:提供了初始化前初始化后

  2. InstantiationAwareBeanPostProcessor:在BeanPostProcessor的基础上增加了实例化前实例化后填充属性后

  3. MergedBeanDefinitionPostProcessor:在BeanPostProcessor的基础上增加了在实例化和实例化后之间的扩展点

相关文章:

  • Linux文件之/etc/passwd和/etc/shadow
  • OCR - 微软windows 11系统自带的Windows OCR功能初体验
  • 公众号网课查题系统
  • 关于SELECT...FOR UPDATE到底锁表还是锁行
  • 解决问题的思路很重要,运维领域,结果对,过程就对!
  • C++ 顺序表和单链表的二路归并思想(详解+示例代码)
  • T1071 菲波那契数(信息学一本通C++)
  • Android开发基础——广播实践
  • opencv 深度学习
  • Windows取证——基本网络命令
  • CDH 09Cloudera Manager Kerberos安装配置(markdown新版二)
  • 小米面试——案例总结
  • 电源硬件设计----升降压变换器(负压输出)基础
  • Nodejs系列之模块加载机制
  • MyBatis 查询数据库入门
  • [译]CSS 居中(Center)方法大合集
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • create-react-app项目添加less配置
  • Electron入门介绍
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • idea + plantuml 画流程图
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • Java到底能干嘛?
  • QQ浏览器x5内核的兼容性问题
  • Ruby 2.x 源代码分析:扩展 概述
  • Selenium实战教程系列(二)---元素定位
  • SQLServer插入数据
  • 前端路由实现-history
  • 前嗅ForeSpider教程:创建模板
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 山寨一个 Promise
  • 责任链模式的两种实现
  • RDS-Mysql 物理备份恢复到本地数据库上
  • 曾刷新两项世界纪录,腾讯优图人脸检测算法 DSFD 正式开源 ...
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • #162 (Div. 2)
  • #git 撤消对文件的更改
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • (1)(1.19) TeraRanger One/EVO测距仪
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (PHP)设置修改 Apache 文件根目录 (Document Root)(转帖)
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (ZT)薛涌:谈贫说富
  • (附源码)springboot 基于HTML5的个人网页的网站设计与实现 毕业设计 031623
  • (附源码)springboot家庭财务分析系统 毕业设计641323
  • (附源码)计算机毕业设计SSM教师教学质量评价系统
  • (四)TensorRT | 基于 GPU 端的 Python 推理
  • (一)C语言之入门:使用Visual Studio Community 2022运行hello world
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • .Net MVC4 上传大文件,并保存表单
  • /etc/fstab 只读无法修改的解决办法
  • @ConfigurationProperties注解对数据的自动封装
  • @JsonSerialize注解的使用
  • [ 蓝桥杯Web真题 ]-布局切换