Spring MVC系列-(4) Bean的生命周期

2023-10-19 20:19:38 浏览数 (2)

4. Bean的生命周期

4.1 Bean的生命周期

Spring中bean的生命周期是指bean创建,初始化,销毁的过程,整个过程是由容器进行管理的。

具体过程如下图所示:

下面对每个过程进行详细介绍:

  1. Spring对Bean进行实例化(相当于程序中的new Xx())
  2. Spring将值和Bean的引用注入进Bean对应的属性中
  3. 如果Bean实现了BeanNameAware接口,Spring将Bean的ID传递给setBeanName()方法(实现BeanNameAware清主要是为了通过Bean的引用来获得Bean的ID,一般业务中是很少有用到Bean的ID的)
  4. 如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanDactory(BeanFactory bf)方法并把BeanFactory容器实例作为参数传入。(实现BeanFactoryAware 主要目的是为了获取Spring容器,如Bean通过Spring容器发布事件等)
  5. 如果Bean实现了ApplicationContextAware接口,Spring容器将调用setApplicationContext(ApplicationContext ctx)方法,把应用上下文作为参数传入.(作用与BeanFactory类似都是为了获取Spring容器,不同的是Spring容器在调用setApplicationContext方法时会把它自己作为setApplicationContext 的参数传入,而Spring容器在调用setBeanDactory前需要程序员自己指定(注入)setBeanDactory里的参数BeanFactory )
  6. 如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessBeforeInitialization(预初始化)方法(作用是在Bean实例创建成功后对进行增强处理,如对Bean进行修改,增加某个功能)
  7. 如果Bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet方法,作用与在配置文件中对Bean使用init-method声明初始化的作用一样,都是在Bean的全部属性设置成功后执行的初始化方法。
  8. 如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessAfterInitialization(后初始化)方法(作用与6的一样,只不过6是在Bean初始化前执行的,而这个是在Bean初始化后执行的,时机不同 )
  9. 经过以上的工作后,Bean将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁
  10. 如果Bean实现了DispostbleBean接口,Spring将调用它的destory方法,作用与在配置文件中对Bean使用destory-method属性的作用一样,都是在Bean实例销毁前执行的方法。

4.2 自定义初始化和销毁方法

Spring中提供了4种自定义Bean的初始化和销毁方法的函数。

  1. 配置类中指定 @Bean(initMethod="init", destroyMethod="destroy")
  2. 让Bean实现 InitializingBeanDisposableBean接口
  3. 使用JSR250规则定义的(java规范)两个注解@PostConstruct@PreDestroy
  4. 利用BeanPostProcessor,通过postProcessBeforeInitializationpostProcessBeforeInitialization在方法初始化前后进行拦截。
1. 配置类中手动指定

以下是定义好的Bike类,

代码语言:javascript复制
public class Bike {
	public Bike(){
		System.out.println("Bike constructor..............");
	}
	public void init(){
		System.out.println("Bike .....init.....");
	}
	public void destory(){
		System.out.println("Bike.....destory");
	}
}

在配置类里通过@Bean(initMethod="init", destroyMethod="destroy")指定初始化和销毁方法:

代码语言:javascript复制
@Bean(initMethod="init", destroyMethod="destory")
public Bike bike(){
	return new Bike();
}

当容器初始化时,会调用对应的init和destory方法进行初始化和销毁。

2. 让Bean实现 InitializingBean 和 DisposableBean接口

A, InitializingBean接口(定义初始化逻辑)

  • afterPropertiesSet()方法:当beanFactory创建好对象,且把bean所有属性设置好之后,会调这个方法,相当于初始化方法

B, DisposableBean接口(定义销毁逻辑)

  • destory()方法,当bean销毁时,会把单实例bean进行销毁

下面的Train.java类中, 实现了InitializingBean和DisposableBean接口。

代码语言:javascript复制
@Component
public class Train implements InitializingBean, DisposableBean{

	public Train(){
		System.out.println("Train......constructor............");
	}
	//当我们bean销毁时,调用此方法
	@Override
	public void destroy() throws Exception {
		System.out.println("Train......destory......");
		//logger.error
	}
	//当我们的bean属性赋值和初始化完成时调用
	@Override
	public void afterPropertiesSet() throws Exception {
		System.out.println("Train.......afterPropertiesSet()...");
		
	}
}

当容器初始化和销毁时,可以看到这两个函数被调用。

3. 可以使用JSR250规则定义的@PostConstruct@PostConstruct注解

@PostConstruct: 在Bean创建完成,且属于赋值完成后进行初始化,属于JDK规范的注解。 @PreDestroy: 在bean将被移除之前进行通知, 在容器销毁之前进行清理工作。

可以参考如下的例子:

代码语言:javascript复制
@Component
public class Jeep {
	public Jeep(){
		System.out.println("Jeep.....constructor........");
	}
	@PostConstruct
	public void init(){
		System.out.println("Jeep.....@PostConstruct........");
	}
	
	@PreDestroy
	public void destory(){
		System.out.println("Jeep.....@PreDestroy......");
	}
}
4. 使用BeanPostProcessor后置处理器

通过postProcessBeforeInitializationpostProcessBeforeInitialization在方法初始化前后进行拦截。

  • postProcessBeforeInitialization(): 在初始化之前进行后置处理工作(在init-method之前),它任何初始化方法调用之前(比如在InitializingBean的afterPropertiesSet初始化之前,或自定义init-method调用之前使用)
  • postProcessAfterInitialization(): 在初始化之后进行后置处理工作, 比如在InitializingBean的afterPropertiesSet()之后。

新建如下例子所示的后置处理器类JamesBeanPostProcessor,

代码语言:javascript复制
@Component
public class JamesBeanPostProcessor implements BeanPostProcessor{
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		//返回一个的对象(传过来的对象)
		//在初始化方法调用之前进行后置处理工作,
		//什么时候调用它: init-method=init之前调用
		System.out.println("postProcessBeforeInitialization...." beanName "..." bean);
		return bean;
	}
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("postProcessAfterInitialization...." beanName "..." bean);
		return bean;
	}
}

从下面的输出结果可以看到,该后置处理器被插入到了所有的bean创建的前后,

代码语言:javascript复制
Jeep.....constructor........
postProcessBeforeInitialization....jeep...com.enjoy.cap7.bean.Jeep@4b44655e
Jeep.....@PostConstruct........
postProcessAfterInitialization....jeep...com.enjoy.cap7.bean.Jeep@4b44655e
Plane.....constructor........
postProcessBeforeInitialization....plane...com.enjoy.cap7.bean.Plane@290d210d
Plane.....@PostConstruct........
postProcessAfterInitialization....plane...com.enjoy.cap7.bean.Plane@290d210d
Train......constructor............
postProcessBeforeInitialization....train...com.enjoy.cap7.bean.Train@10db82ae
Train.......afterPropertiesSet()...
postProcessAfterInitialization....train...com.enjoy.cap7.bean.Train@10db82ae
给容器中添加person.......
postProcessBeforeInitialization....person...Person [name=person, age=20]
postProcessAfterInitialization....person...Person [name=person, age=20]
Bike constructor..............
postProcessBeforeInitialization....bike...com.enjoy.cap7.bean.Bike@60dcc9fe
Bike .....init.....
postProcessAfterInitialization....bike...com.enjoy.cap7.bean.Bike@60dcc9fe

BeanPostProcessor源码分析:

从上述的结果可以看到,自定义的BeanPostProcessor对在每个Bean的创建过程中都起到了作用,为什么会产生这样的效果?以下是源码中对BeanPostProcessor的调用过程:

  • AnnotationConfigApplicationContext-->refresh()-->
  • finishBeanFactoryInitialization(beanFactory)--->
  • beanFactory.preInstantiateSingletons()-->
  • getBean(beanName)--->
  • doGetBean(name, null, null, false)-->
  • createBean(beanName, mbd, args)-->
  • doCreateBean(beanName, mbdToUse, args)-->
  • 541行createBeanInstance(beanName, mbd, args)(完成bean创建)-->
  • 578行populateBean(beanName, mbd, instanceWrapper)(属性赋值)-->
  • 579行initializeBean(beanName, exposedObject, mbd)(Bean初始化)->
  • 1069行到1710行,后置处理器完成对init方法的前后处理.

从下面的程序流程可以看到,就是在执行initMethod方法的前后进行BeanPostProcess方法的处理。

4.3 BeanPostProcess在Spring中的应用

上一节介绍了可以通过自定义BeanPostProcess,来实现对Bean生命周期的控制。这一节具体介绍BeanPostProcess在Spring中的应用。

下面是Plane的定义,以此为例来介绍BeanPostProcess的应用。

代码语言:javascript复制
@Component
public class Plane implements ApplicationContextAware{
	private ApplicationContext applicationContext;
	public Plane(){
		System.out.println("Plane.....constructor........");
	}
	@PostConstruct
	public void init(){
		System.out.println("Plane.....@PostConstruct........");
	}
	
	@PreDestroy
	public void destory(){
		System.out.println("Plane.....@PreDestroy......");
	}
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		//将applicationContext传进来,可以拿到
		this.applicationContext = applicationContext;
	}
}

和之前的分析一样,IOC容器在初始化Bean之前,会遍历所有的BeanPostProcessor并执行相应的操作:

断点查看可以发现所有的BeanPostProcess,

下面介绍的BeanPostProcess都是在这里被调用。

1. ApplicationContextAwareProcessor

这个BeanProcess发生在下面的函数调用时,

代码语言:javascript复制
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		//将applicationContext传进来,可以拿到
		this.applicationContext = applicationContext;
	}

打断点可以看到该BeanProcess为ApplicationContextAwareProcessor。

此类帮我们组建IOC容器,跟进ApplicationContextAwareProcessor我们发现, 这个后置处理器其实就是判断我们的bean有没有实现ApplicationContextAware 接口,并处理相应的逻辑,其实所有的后置处理器原理均如此。 那么怎么组建呢? 只需要实现 ApplicationContextAware接口。上面给出的Plane中已经实现了这个接口。

  • a,在创建Plane对象,还没初始化之前, 先判断是不是实现了ApplicationContextAware接口, 如果是的话就调用invokeAwareInterfaces方法, 并给里面注入值;
  • b,进入invokeAwareInterfaces()方法,判断是哪个aware, 如果是ApplicationContextAware, 就将当前的bean转成ApplicationContextAware类型, 调用setApplicationContext(), 把IOC容器注入到Plane里去;

2. InitDestroyAnnotationBeanPostProcessor

此处理器用来处理@PostConstruct, @PreDestroy, 怎么知道这两注解是前后开始调用的呢, 就是 InitDestroyAnnotationBeanPostProcessor这个处理的。

以@PostConstruct为例, 为什么声明这个注解后就能找到初始化init方法呢?

3. BeanValidationPostProcess

这个BeanPostProcess在Plane这个Bean的创建过程中没有用到,但是在WEB中用得特别多,通常用于把页面提交的值进行校验。

总结: Spring底层对BeanPostProcessor的使用, 包括bean的赋值, 注入其它组件, 生命周期注解功能,@Async等等。

4.4 BeanDefinition作用

BeanDefinition 是 Spring 内部的一个接口,定义了 Bean 描述的基本规范,值得关注的主要方法包括:

  • getBeanClassName:获取 Bean 的类名称;
  • isLazyInit:是否懒加载;
  • getFactoryBeanName:获取对应的 FactoryBean 的名称;
  • getConstructorArgumentValues:获取构造器参数,可以通过 xml、注解注入;
  • getPropertyValues:获取属性参数,可以通过 xml、注解注入。

可以看到 BeanDefinition 接口的方法覆盖了 Spring 构造 Bean 需要的所有信息,是一个什么样的类型,构造器有哪些实参,属性的值注入哪些值,使用哪个 FactoryBean 来获取 Bean等。

BeanDefinition 的接口定义和实现类如图所示,整体也是分层设计。基于注解的 Bean 定义使用 AnnotatedBeanDefinition 描述,定义了获取具体 Method 的方法。

BeanDefinition 描述了一个 Bean 的定义,实际上就是对象在容器中的抽象。通过这个 BeanDefinition 定义的数据结构,容器能够方便地对 Bean 进行管理。对于单例的 Bean,在第一次 getBean 的时候读取 BeanDefinition 获取定义,然后再创建和初始化 Bean,并缓存起来。对于原型的 Bean,在每一次 getBean 的时候都需要使用 BeanDefinition 的数据进行创建和初始化,不会缓存 Bean。

BeanDefinition 在 Spring 内部使用,外部开发者一般感知不到 BeanDefinition 的存在。我们使用时只需要关注通过 xml、注解怎样定义了一个 Bean,然后使用即可。而这中间的解析、创建、初始化都通过 BeanDefinition 来承载。理解 BeanDefinition 是理解 Spring 基本原理的重要环节。

0 人点赞