浅谈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对象的实例化步骤,还是直接跳转到初始化后的步骤。



总结:

  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在处理与属性相关的操作时单独操作,在处理逻辑相关时集中处理,貌似又有那么一点分层的味道。

以上是关于浅谈Spring中Bean的生命周期的主要内容,如果未能解决你的问题,请参考以下文章

springbean的生命周期是啥?

Spring应用上下文中Bean的生命周期

Spring课程 Spring入门篇 3-2 Spring bean装配(上)之bean的生命周期

spring中bean的生命周期是怎么样的

深究Spring中Bean的生命周期

Spring事务,Bean生命周期