浅谈Spring中Bean的生命周期
Posted 默辨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈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对象的实例化步骤,还是直接跳转到初始化后的步骤。
总结:
- 一个BeanPostProcess接口,含有实例化前和实例化后两个接口
- 对象的实例化前操作返回不为null,则直接进行初始化后操作
- beanPostProcessors集合,存放了与BeanPostProcessor接口相关类,用于遍历
3.2、实例化
根据前面的流程图可以得出,后续生命周期步骤都在doCreateBean方法内完成。
调用createBeanInstance方法,完成它实例化操作
1
具体的实例化细节比较繁杂,它会去推断构造方法,然后再根据构造方法去反射创建我们的对象,这即是所说的实例化过程。
2
实例化方法调用前后的变化
3
3.1、实例化方法完成前:instanceWrapper为null
3.2、实例化方法完成后:instanceWrapper是我们的目标对象
根据 这三步,我们可以得出结论,createBeanInstance方法,能够实例化出我们的对象(属性还没有具体的值)在我们实例化Bean的操作完成后,会将结果赋值给instanceWrapper变量。
根据截图我们也不难发现,instanceWrapper变量可以当作是我们实例化出来对象的封装体。想要拿到我们期望创建的Bean对象,调用getWrappedClass方法即可。
总结:
- 实例化对象是根据构造方法反射创建而出
- 实例化方法返回的对象是我们期望实例化对象的封装体,一定要和期望实例化出来的对象区分开
3.3、实例化后
首先明确,我们的User对象信息
先来看功能扩展,该步骤与实例化前执行逻辑大体相同。
1
与实例化前相同,实例化后也有相同的操作,执行的流程和实例化前大体相同,这里就不再赘述。如果你足够细心,在前面展示实例化前接口截图的时候你就应该已经发现,实例化前和实例化后的操作存在某种共性。不信的话,我让你再看一遍这个接口。
实例化方法populateBean可以分成两部分,功能扩展和属性填充
先看属性填充效果
2
我们可以看到,由于User对象内部依赖了另一个对象Cat,所以当我们在调试代码的时候,会发现,Spring会先去找Cat对象,即去处理Cat的生命周期,当Cat的生命周期执行完成之后才会来继续完成我们User对象的实例化后操作。因为这一步涉及属性的填充,Spring要先去获取我们需要填充的属性Cat,继而完成对当前对象User的属性填充,执行完该步骤,对象的赋值操作也就完成了。
方法内部具体的属性注入步骤
3
总结:
- 实例化后与实例化前有着类似的扩展功能,它们分别是BeanPostProcess接口下的两个方法
- 实例化的关键接口——BeanPostProcess
- Bean属性填充是在实例化之后完成
- 根据代码逻辑,Spring的实例化扩展操作在属性填充操作之后
3.4、初始化前前
在invokeAwareMethods方法内部完成
1
判断对应的Bean对象是实现了哪一个接口,强转为对应的接口,执行对应的方法。如果都继承了,那就依次执行
2
总结:
- 实现Aware接口的子接口,重写对应方法,完成值的set
3.5、初始化前、中、后
同样还是在该方法中,完成了初始化的所有步骤,该步骤比较简单,直接放截图
0
初始化前,遍历beanPostProcessors集合,调用postProcessBeforeInitialization方法
1、初始化前
初始化,调用每个Bean的afterPropertiesSet方法
2、初始化中
初始化前,遍历beanPostProcessors集合,调用postProcessAfterInitialization方法
3、初始化后
区分与实例化前的接口区别,BeanPostProcessor接口是实例化前接口InstantiationAwareBeanPostProcessor的父类
4
当熟悉了实例化前后的操作,我们会发现初始化前中后的处理操作都差不多,步骤都可以分为这几步
- 实现了指定的接口
- 遍历构建好的集合(同一个集合),再调用集合中对象对应的方法
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在处理与属性相关的操作时单独操作,在处理逻辑相关时集中处理,貌似又有那么一点分层的味道。
以上是关于浅谈Spring中Bean的生命周期的主要内容,如果未能解决你的问题,请参考以下文章