首页 > 其他分享 >浅谈Spring中Bean的生命周期

浅谈Spring中Bean的生命周期

时间:2023-01-19 11:02:38浏览次数:41  
标签:初始化 浅谈 Spring 化前 接口 Bean 实例 println out


文章目录

  • ​​1、基本概念​​
  • ​​2、生命周期流程图​​
  • ​​3、源码与功能分析​​
  • ​​3.1、实例化前​​
  • ​​3.2、实例化​​
  • ​​3.3、实例化后​​
  • ​​3.4、初始化前前​​
  • ​​3.5、初始化前、中、后​​
  • ​​4、核心接口的简单代码实现​​
  • ​​4.1、简单实现​​
  • ​​4.2、简单再实现​​
  • ​​4.3、结论​​




1、基本概念

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


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

本文主要讲述Spring中将一个类注册到Spring容器中的关键步骤,外界称它为Bean的生命周期。我将Spring的生命周期分为实例化前(扩展)、实例化中(实例化对象,走构造方法)、实例化后(属性填充 + 扩展)、初始化前前(完成部分属性回填)、初始化前(扩展)、初始化中(完善部分属性值)、初始化后(扩展),共7步,严格来说,这里还应该加上销毁阶段,本文先不考虑该阶段


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

浅谈Spring中Bean的生命周期_初始化



2、生命周期流程图

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

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

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

浅谈Spring中Bean的生命周期_初始化_02



3、源码与功能分析

3.1、实例化前

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

1

浅谈Spring中Bean的生命周期_初始化_03


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

2

浅谈Spring中Bean的生命周期_System_04


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

3

浅谈Spring中Bean的生命周期_实例化_05

浅谈Spring中Bean的生命周期_初始化_06


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

浅谈Spring中Bean的生命周期_System_07


总结:

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


3.2、实例化

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

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

1

浅谈Spring中Bean的生命周期_System_08


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

2

浅谈Spring中Bean的生命周期_实例化_09


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

3

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

浅谈Spring中Bean的生命周期_System_10



3.2、实例化方法完成后:instanceWrapper是我们的目标对象

浅谈Spring中Bean的生命周期_初始化_11

浅谈Spring中Bean的生命周期_System_12



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

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



总结:

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


3.3、实例化后

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

浅谈Spring中Bean的生命周期_System_13


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

1

浅谈Spring中Bean的生命周期_实例化_14

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

浅谈Spring中Bean的生命周期_实例化_15


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

先看属性填充效果

2

浅谈Spring中Bean的生命周期_实例化_16

浅谈Spring中Bean的生命周期_System_17

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


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

3

浅谈Spring中Bean的生命周期_System_18



总结:

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


3.4、初始化前前

在invokeAwareMethods方法内部完成

1

浅谈Spring中Bean的生命周期_实例化_19


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

2

浅谈Spring中Bean的生命周期_实例化_20



总结:

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


3.5、初始化前、中、后

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

0

浅谈Spring中Bean的生命周期_初始化_21


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

1、初始化前

浅谈Spring中Bean的生命周期_实例化_22


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

2、初始化中

浅谈Spring中Bean的生命周期_System_23


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

3、初始化后

浅谈Spring中Bean的生命周期_System_24


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

4

浅谈Spring中Bean的生命周期_初始化_25



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

  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…可测试结果真的是这样吗?



测试结果:

浅谈Spring中Bean的生命周期_System_26


根据测试结果发现,我们会先执行第二步、第四步、第六步,也就是实例化中、初始化前前、初始化中这三步.对应的属性赋值也测试正确,最后开始遍历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、再次测试

浅谈Spring中Bean的生命周期_System_27



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

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


4.3、结论

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

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


集中操作:InstantiationAwareBeanPostProcessor接口(4)

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

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


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



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


标签:初始化,浅谈,Spring,化前,接口,Bean,实例,println,out
From: https://blog.51cto.com/u_15942107/6019951

相关文章

  • SpringCloud(二)
    文章目录​​6、Ribbon​​​​6.1、Ribbon是什么​​​​6.2、Ribbon能干什么​​​​6.3、Ribbon实现负载均衡环境搭建​​​​6.4、Ribbon实现负载均衡​​​​6.5、自定......
  • Spring
    全文大纲​​1、Spring​​​​1.1、简介​​​​1.2、优点​​​​1.3、组成(七大模块)​​​​1.4、拓展​​​​2、IOC理论推导​​​​2.1、最初的开发​​​​2.2、改进......
  • 学习笔记——Spring声明式事务管理属性(隔离级别、事务超时、事务只读、事务回滚);Spring
    2023-01-19Spring声明式事务管理属性一、隔离级别1、概念:一个事务与其他事务之间的隔离等级(1,2,4,8)。2、隔离级别:(1)读未提交(1):READUNCOMMTTED存在问题:脏读(读取到了未......
  • ElasticSearch全文搜索引擎 -Spring Boot操作ES(SpringData概述、Spring Data Elastics
    文章目录​​1.SpringData概述​​​​2.SpringDataElasticsearch​​​​3.实现基本操作​​​​4.ElasticSearch操作文档​​​​5.ElasticSearch原生API操作工具......
  • Spring Batch多步骤任务、并行执行、任务决策器、任务嵌套
    道虽迩(ěr),不行不至,事虽小,不为不成......
  • Spring-webflux 响应式编程
    热爱可抵漫长岁月文章目录​​1.前言​​​​2.Spring-webflux简介​​​​3.什么是“响应式”​​​​4.Spring-webflux的响应式API​​​​5.SpringMVC还是WebFlu......
  • Spring自定义启动图标(个性化启动图标)
     今天比昨天好,这就是希望。☀️☀️☀️ 文章目录​​个性化输出​​​​图片转换输出​​​​生成banner的网站​​Spring默认启动输出.________......
  • SpringCloud Tencent Polaris
    北极星是腾讯开源的服务发现和治理中心,致力于解决分布式或者微服务架构中的服务可见、故障容错、流量控制和安全问题。虽然,业界已经有些组件可以解决其中一部分问题,但是缺少......
  • SpringBoot+MyBatils-puls+db2
    记录自己搭建db2数据库,链接数据库遇到的问题。 搭建db2我是在阿里云使用docker搭建的db2数据库,搭建数据库参考下面这个链接https://www.cnblogs.com/Yongzhouunknown/p......
  • 14.spring声明事务
    1.要开启Spring的事务处理功能,在Spring的配置文件中创建一个DataSourceTransactionManager 对象:<!--配置声明式事务--><beanid="transationManager"class="org.s......