IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 浅谈Spring中Bean的生命周期 -> 正文阅读

[Java知识库]浅谈Spring中Bean的生命周期





1、基本概念

你只需要明白Bean生命周期的执行逻辑即可,不需要死磕对应的流程步骤及流程名字,希望本文对你有帮助。



在初学Java时,我们需要new一个类,即可得到一个对象。学到框架时,我们只需要将类注册到Spring容器中,即可得到一个对象。

本文主要讲述Spring中将一个类注册到Spring容器中的关键步骤,外界称它为Bean的生命周期。我将Spring的生命周期分为实例化前(扩展)、实例化中(实例化对象,走构造方法)、实例化后(属性填充 + 扩展)、初始化前前(完成部分属性回填)、初始化前(扩展)、初始化中(完善部分属性值)、初始化后(扩展),共7步



单就实例化和初始化两个词语的概念,一定要和JVM规范中的实例化和初始化区分开。JVM规范中类是先进行初始化,再进行实例化。而Spring中是先进行实例化,再进行初始化。Spring中Bean的实例化包含了JVM规范中的初始化和实例化。我们可以通过构造方法去观察,JVM中构造方法属于JVM规范中的实例化步骤,在Spring中Bean的生命周期步骤属于实例化步骤。当然你还可以通过静态代码块进行测试,在JVM中静态代码块属于初始化步骤,在Spring中Bean的生命周期中,处在实例化前和实例化后之间。

请添加图片描述





2、生命周期流程图

根据源代码,画出部分流程图

采用配置类的方式启动Spring容器,所以以AnnotationConfigApplicationContext类为起点

对应的后置处理器(BeanpostProcessor)集合在refresh方法中 --> prepareBeanFactory方法中 --> addBeanPostProcessor方法中进行构建,图中未给出,后文会涉及该集合

请添加图片描述





3、源码与功能分析

3.1、实例化前

在createBean方法中调用resolveBeforeInstantiation方法,完成Bean对象的实例化前操作

1

请添加图片描述



如果实例化对象后的结果不为null(我们自己实现了BeanPostProcessor接口,实现自定义的实例化前逻辑),那么该Bean就会直接进行初始化后的操作,即此处的applyBeanPostProcessorsAfterInitialization方法

2

请添加图片描述



调用postProcessBeforeInstantiation方法,完成实例化前操作

3

请添加图片描述

请添加图片描述



根据最后两张图我们可以发现,applyBeanPostProcessorsBeforeInstantiation方法会去遍历beanPostProcessors集合,然后强转为BeanPostProcess接口(Bean的后置处理器)的子接口,最后再调用接口对应的postProcessBeforeInstantiation方法,该方法默认返回为null。该返回值就间接影响了我们第二步中的返回值,我们的程序究竟是继续完成Bean对象的实例化步骤,还是直接跳转到初始化后的步骤。

请添加图片描述



总结:

  1. 一个BeanPostProcess接口,含有实例化前和实例化后两个接口
  2. 对象的实例化前操作返回不为null,则直接进行初始化后操作
  3. beanPostProcessors集合,存放了与BeanPostProcessor接口相关类,用于遍历




3.2、实例化

根据前面的流程图可以得出,后续生命周期步骤都在doCreateBean方法内完成。

调用createBeanInstance方法,完成它实例化操作

1

请添加图片描述



具体的实例化细节比较繁杂,它会去推断构造方法,然后再根据构造方法去反射创建我们的对象,这即是所说的实例化过程。

2

请添加图片描述



实例化方法调用前后的变化

3

3.1、实例化方法完成前:instanceWrapper为null

请添加图片描述


3.2、实例化方法完成后:instanceWrapper是我们的目标对象
请添加图片描述请添加图片描述


根据 这三步,我们可以得出结论,createBeanInstance方法,能够实例化出我们的对象(属性还没有具体的值)在我们实例化Bean的操作完成后,会将结果赋值给instanceWrapper变量。

根据截图我们也不难发现,instanceWrapper变量可以当作是我们实例化出来对象的封装体。想要拿到我们期望创建的Bean对象,调用getWrappedClass方法即可。


总结:

  1. 实例化对象是根据构造方法反射创建而出
  2. 实例化方法返回的对象是我们期望实例化对象的封装体,一定要和期望实例化出来的对象区分开




3.3、实例化后

首先明确,我们的User对象信息

请添加图片描述



先来看功能扩展,该步骤与实例化前执行逻辑大体相同。

1

请添加图片描述

与实例化前相同,实例化后也有相同的操作,执行的流程和实例化前大体相同,这里就不再赘述。如果你足够细心,在前面展示实例化前接口截图的时候你就应该已经发现,实例化前和实例化后的操作存在某种共性。不信的话,我让你再看一遍这个接口。

请添加图片描述



实例化方法populateBean可以分成两部分,功能扩展和属性填充

先看属性填充效果

2

请添加图片描述

请添加图片描述

我们可以看到,由于User对象内部依赖了另一个对象Cat,所以当我们在调试代码的时候,会发现,Spring会先去找Cat对象,即去处理Cat的生命周期,当Cat的生命周期执行完成之后才会来继续完成我们User对象的实例化后操作。因为这一步涉及属性的填充,Spring要先去获取我们需要填充的属性Cat,继而完成对当前对象User的属性填充,执行完该步骤,对象的赋值操作也就完成了



方法内部具体的属性注入步骤

3

请添加图片描述


总结:

  1. 实例化后与实例化前有着类似的扩展功能,它们分别是BeanPostProcess接口下的两个方法
  2. 实例化的关键接口——BeanPostProcess
  3. Bean属性填充是在实例化之后完成
  4. 根据代码逻辑,Spring的实例化扩展操作在属性填充操作之后




3.4、初始化前前

在invokeAwareMethods方法内部完成

1

请添加图片描述



判断对应的Bean对象是实现了哪一个接口,强转为对应的接口,执行对应的方法。如果都继承了,那就依次执行

2

请添加图片描述


总结:

  1. 实现Aware接口的子接口,重写对应方法,完成值的set




3.5、初始化前、中、后

同样还是在该方法中,完成了初始化的所有步骤,该步骤比较简单,直接放截图

0

请添加图片描述



初始化前,遍历beanPostProcessors集合,调用postProcessBeforeInitialization方法

1、初始化前

请添加图片描述



初始化,调用每个Bean的afterPropertiesSet方法

2、初始化中

请添加图片描述



初始化前,遍历beanPostProcessors集合,调用postProcessAfterInitialization方法

3、初始化后

请添加图片描述



区分与实例化前的接口区别,BeanPostProcessor接口是实例化前接口InstantiationAwareBeanPostProcessor的父类

4

请添加图片描述


当熟悉了实例化前后的操作,我们会发现初始化前中后的处理操作都差不多,步骤都可以分为这几步

  1. 实现了指定的接口
  2. 遍历构建好的集合(同一个集合),再调用集合中对象对应的方法




4、核心接口的简单代码实现

4.1、简单实现

spring容器启动类:

public class MainTest {
   public static void main(String[] args) {

      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
      System.out.println(context.getBean("student"));

   }
}

配置类:

@ComponentScan("pers.mobian.springeighth")
public class Config {

}



实体类:

实现了对应的接口,然后依次重写对应的方法

InstantiationAwareBeanPostProcessor:是BeanPostProcessor的子类,包含实例化前、实例化后、初始化前、初始化后方法

InitializingBean:包含afterPropertiesSet方法,用于初始化

BeanNameAware、BeanClassLoaderAware、BeanFactoryAware:均是Aware接口的子类

@Component
public class Student implements InstantiationAwareBeanPostProcessor, InitializingBean, BeanNameAware, BeanClassLoaderAware, BeanFactoryAware {

	@Value("mobian")
	private String name;

	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		System.out.println(beanName);
		System.out.println("1、实例化前");
		return null;
	}
	public Student() {
		System.out.println("2、实例化zhong:"+name);
	}
	@Override
	public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
		System.out.println("3、实例化后");
		return false;
	}


    
    
	@Override
	public void setBeanName(String name) {
		System.out.println("4.1、初始化前前,设置beanName");
		System.out.println(name);
		System.out.println(this.name);
	}
	@Override
	public void setBeanClassLoader(ClassLoader classLoader) {
		System.out.println("4.2、初始化前前,设置classLoader");
		System.out.println(classLoader);
	}
	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		System.out.println("4.3、初始化前前,设置beanFactory");
		System.out.println(beanFactory);
	}

    
    

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("5、初始化前");
		return null;
	}
	@Override
	public void afterPropertiesSet() throws Exception {
		System.out.println("6、初始化zhong:"+name);
	}
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("7、初始化后");
		return null;
	}
}

按照前面的分析,我的打印结果应该的顺序是123…可测试结果真的是这样吗?


测试结果:
请添加图片描述



根据测试结果发现,我们会先执行第二步、第四步、第六步,也就是实例化中、初始化前前、初始化中这三步.对应的属性赋值也测试正确,最后开始遍历BeanPostProcessor集合,依次执行剩下的步骤。说好的顺序执行呢?代码逻辑没错呀。

于是我又开始了新的思考,通过观察我发现了一个奇怪的点:我们的student对象呢?理论上说,哪怕是不顺序执行,你起码要给我执行一次呀,可是根本一次也没有。这又是为什么呢,把自己屏蔽呢?




于是我又开始了接下来的一个新测试

4.2、简单再实现

1、掉Student类中的InstantiationAwareBeanPostProcessor接口,当然对应的方法也要去掉

2、新建一个Teacher类

@Component
public class Teacher implements InstantiationAwareBeanPostProcessor {

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

	@Override
	public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
		System.out.println("3、实例化后");
		return true;
	}



	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("5、初始化前");
		return null;
	}
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("7、初始化后");
		return null;
	}
}

3、再次测试
请添加图片描述


巧了,这不就是我们想要的结果,按照顺序打印。

前面多余的测试结果,我们只需要在对应的代码中添加判断即可准确的定位到我们想要的student对象。




4.3、结论

至此,对于Spring的生命周期我们能否有新的理解?

除了上面的代码顺序理解以外。我们还可以将这些方法分为自身操作和集中操作两部分。



集中操作:InstantiationAwareBeanPostProcessor接口(4)

单独操作:InitializingBean接口、Aware接口的子接口以及构造方法(1+1+1)

如果这样分类,其实解决了我最开始的看源码逻辑时的一个疑惑。不知道你有没有注意,我们在调用实例化前后、初始化前后方法时,都是去遍历BeanPostProcessor集合,但执行其他方法的时候却是单独的一条线。反正我最开始是有这个疑问。那么



在一次完整Bean的生命周期过程中,它会依次完成实例化和初始化的步骤(本文分类7步)。但在这个过程中,处理实例化前后、初始化前后流程时,会调用所有实现了BeanPostProcessor接口的方法,处理相应的逻辑;处理实例化中、初始化前前、初始化中的过程,则为每个实体单独的处理操作。


如果想要让集中操作和单独操作能体现在一个类上,那么就需要使用不同的类来处理(一个类同时实现两个接口无法达到对应的效果),Bean在处理与属性相关的操作时单独操作,在处理逻辑相关时集中处理,貌似又有那么一点分层的味道。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-07-26 11:56:40  更:2021-07-26 11:57:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年3日历 -2024/3/29 22:41:47-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码