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

Spring Bean的生命周期

时间:2022-12-03 19:57:24浏览次数:42  
标签:生命周期 Spring Object bean 接口 Bean throws

说明: 本文基于Spring-Framework 5.1.x版本讲解

概述

说起生命周期, 很多开源框架、中间件的组件都有这个词,其实就是指组件从创建到销毁的过程。 那这里讲Spring Bean的生命周期,并不是讲Bean是如何创建的, 而是想讲下Bean从实例化到销毁,Spring框架在Bean的各个阶段给我们提供了哪些拓展点。 Bean本身有三个大的阶段: 实例化、初始化、销毁。

Spring的强大就是提供了非常多的拓展点, 我们可以基于这些拓展点实现不同的需求。 回到主题,Spring容器围绕着生命周期的各个阶段提供了不同功能的拓展点如下图:

从上图可以看到,整个生命周期涉及到的接口(当然这不是所有接口,只是日常中可以用到的,还有一部分是Spring内部的接口)分成了4个阶段:

1. 实例化阶段: 主要是以不同方式实例化Bean

2. 属性注入阶段:IOC的过程

3. 初始化阶段:初始化Bean的内部组件、生成代理对象等都在这里

4. 销毁阶段: 释放资源

下面我们对这些核心的接口进行简单的介绍

 

接口介绍

InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation

@Nullable
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
	return null;
}

返回值Object: 返回null值则执行Spring提供给我们的Bean实例化、属性注入、初始化阶段,  这是对于大多数Bean的选择; 返回非null值则跳过Bean实例化、属性注入、初始化阶段,因为Spring会认为IOC等阶段由使用者管理,所以在这种情况下后续会直接调用BeanPostProcessor#postProcessAfterInitialization接口, 进而有可能提前结束标准IOC流程, 为什么说有可能呢? 因为还需取决于BeanPostProcessor#postProcessAfterInitialization的返回值。

使用场景: 使用频率比较低,实际没有用到过。不过在Spring-Framework的AOP源码中有用到,见AbstractAutoProxyCreator

 

InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation

default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
	return true;
}

返回值boolean: true则执行Spring的属性注入功能 , 大多数情况返回true即可,除非你有定制化的需求;返回false跳过Spring属性注入功能,意味着@Resource、@Autowired等注解失效,也意味着xml文件中<property>标签失效。

使用场景: 比较少, Spring-Framework源码中也没有用到

 

InstantiationAwareBeanPostProcessor#postProcessProperties

@Nullable
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
	return null;
}

返回值PropertyValues: 一般返回入参pvs即可, Spring后续会使用返回值进行属性注入。 尤其对于SpringBoot这种纯JavaConfig配置的方式, 参数pvs一般为Empty(注意与null区分)

使用场景:标注@Resource、@Autowired等注解的属性都在这个方法实现注入,比较核心, 详见: CommonAnnotationBeanPostProcessor、AutowiredAnnotationBeanPostProcessor 

 

如果postProcessProperties方法返回null,Spring也会调用postProcessPropertyValues方法实现同样的效果,不过这个方法是老版本中的,已经弃用掉的,不推荐使用. 

@Deprecated
@Nullable
default PropertyValues postProcessPropertyValues(
		PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
	return pvs;
}

 

BeanName、BeanFactory、BeanClassLoaderAware

public interface BeanNameAware extends Aware {
	void setBeanName(String name);
}
public interface BeanClassLoaderAware extends Aware {
	void setBeanClassLoader(ClassLoader classLoader);
}
public interface BeanFactoryAware extends Aware {
	void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}

这三个接口就比较简单了,就是给我们的Bean注入BeanName、ClassLoader、BeanFactory属性, 他的调用时机在实例化、属性注入之后,是初始化阶段的第一步

 

BeanPostProcessor#postProcessBeforeInitialization

/**
 * Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
 * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
 * or a custom init-method). The bean will already be populated with property values.
 * The returned bean instance may be a wrapper around the original.
 * <p>The default implementation returns the given {@code bean} as-is.
 * @param bean the new bean instance
 * @param beanName the name of the bean
 * @return the bean instance to use, either the original or a wrapped one;
 * if {@code null}, no subsequent BeanPostProcessors will be invoked
 * @throws org.springframework.beans.BeansException in case of errors
 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
 */
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
	return bean;
}

返回值Object: 一般情况返回入参的bean即可。

需要注意几点:

1. 如果返回null,则后续所有实现该接口的Processor都不会执行,且返回上个Processor的返回值;

2. 上个Processor的返回值会作为下个Processor的入参。

3. 最后一个Processor的返回值会代替原来的Bean(返回入参Bean的情况可以忽略这一条)进行后续处理(包含Bean初始化、以及最终暴露到Spring容器中)

这里比较难理解,贴下源码

/**
  * 执行Bean的初始化步骤
  */
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
   
    // 此处省略不关键的部分代码
    
	Object wrappedBean = bean;
    // 调用所有实现BeanPostProcessor#postProcessBeforeInitialization接口的方法
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    // 使用postProcessBeforeInitialization方法的返回值当做入参进行Bean初始化
	invokeInitMethods(beanName, wrappedBean, mbd);
    // 使用postProcessBeforeInitialization方法的返回值当做入参进行Bean初始化的后置处理
	wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
	return wrappedBean;
}
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
	Object result = existingBean;
    // getBeanPostProcessors() 返回的Processors是有有优先级顺序的
	for (BeanPostProcessor processor : getBeanPostProcessors()) { 
		Object current = processor.postProcessBeforeInitialization(result, beanName);
		if (current == null) {
			return result; // 如果为null , 这里直接退出该方法并返回上次Processor返回的结果
		}
		result = current; // 上个Processor执行的结果作为下个Processor处理的入参
	}
	return result;
}

使用场景:1. 标注@PostConstruct注解的方法都在该接口实现调用, 详见InitDestroyAnnotationBeanPostProcessor 2. 各种Aware接口的调用入口,详见ApplicationContextAwareProcessor

 

InitializingBean#afterPropertiesSet

public interface InitializingBean {

	/**
	 * Invoked by the containing {@code BeanFactory} after it has set all bean properties
	 * and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
	 * <p>This method allows the bean instance to perform validation of its overall
	 * configuration and final initialization when all bean properties have been set.
	 * @throws Exception in the event of misconfiguration (such as failure to set an
	 * essential property) or if initialization fails for any other reason
	 */
	void afterPropertiesSet() throws Exception;

}

Bean初始化资源的回调接口 , 与@PostConstruct一样的作用 ,但要注意的是几个初始化方法调用的顺序:1.@PostConstruct2. afterPropertiesSet 3. init-method

使用场景: 资源初始化、可以使用回调的特性实现策略模式等,实际工作中用的较多

 

init-method

XML配置文件中init-method属性或者@Bean注解initMethod属性指定的方法 , 比较简单不多说了

 

BeanPostProcessor#postProcessAfterInitialization

/**
 * Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
 * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
 * or a custom init-method). The bean will already be populated with property values.
 * The returned bean instance may be a wrapper around the original.
 * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
 * instance and the objects created by the FactoryBean (as of Spring 2.0). The
 * post-processor can decide whether to apply to either the FactoryBean or created
 * objects or both through corresponding {@code bean instanceof FactoryBean} checks.
 * <p>This callback will also be invoked after a short-circuiting triggered by a
 * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
 * in contrast to all other BeanPostProcessor callbacks.
 * <p>The default implementation returns the given {@code bean} as-is.
 * @param bean the new bean instance
 * @param beanName the name of the bean
 * @return the bean instance to use, either the original or a wrapped one;
 * if {@code null}, no subsequent BeanPostProcessors will be invoked
 * @throws org.springframework.beans.BeansException in case of errors
 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
 * @see org.springframework.beans.factory.FactoryBean
 */
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
	return bean;
}

返回值Object: 一般情况返回入参的bean即可。

postProcessBeforeInitialization方法的调用逻辑一样,需要注意几点:

1. 如果返回null,则后续所有实现该接口的Processor都不会执行,且返回上个Processor的返回值;

2. 上个Processor的返回值会作为下个Processor的入参。

3. 最后一个Processor的返回值会代替原来的Bean(返回入参Bean的情况可以忽略这一条)进行后续处理(包含Bean初始化、以及最终暴露到Spring容器中)

使用场景:AOP生成代理的入口,详见AbstractAutoProxyCreator

 

DestructionAwareBeanPostProcessor#postProcessBeforeDestruction

/**
 * Apply this BeanPostProcessor to the given bean instance before its
 * destruction, e.g. invoking custom destruction callbacks.
 * <p>Like DisposableBean's {@code destroy} and a custom destroy method, this
 * callback will only apply to beans which the container fully manages the
 * lifecycle for. This is usually the case for singletons and scoped beans.
 * @param bean the bean instance to be destroyed
 * @param beanName the name of the bean
 * @throws org.springframework.beans.BeansException in case of errors
 * @see org.springframework.beans.factory.DisposableBean#destroy()
 * @see org.springframework.beans.factory.support.AbstractBeanDefinition#setDestroyMethodName(String)
 */
void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;

用于处理Bean销毁前的一些前置工作,可实现批量处理。

使用场景:标注@PreDestroy注解的方法都在该接口实现调用, 详见InitDestroyAnnotationBeanPostProcessor

 

DisposableBean#destroy

/**
 * Invoked by the containing {@code BeanFactory} on destruction of a bean.
 * @throws Exception in case of shutdown errors. Exceptions will get logged
 * but not rethrown to allow other beans to release their resources as well.
 */
void destroy() throws Exception;

Bean直接实现该接口即可,同时也注意这几个销毁方法的调用顺序: 1.@PreDestroy2. destroy 3. destroy-method

使用场景:释放Bean本身持有的资源,如连接池Bean资源等 ,单个处理。

 

destroy-method

XML配置文件中destroy-method属性或者@Bean注解destroyMethod属性指定的方法 , 比较简单不多说了

 

小结

围绕生命周期Spring容器给我们提供的接口就简单介绍到这,实际上除了上面列举的接口之外,还有一些不常用的接口我没有列举出来,如:SmartInstantiationAwareBeanPostProcessor、MergedBeanDefinitionPostProcessor等。对于以上接口,个人理解没有必要像八股文一样死记硬背每个接口的作用,甚至尝试记住接口的名称,关键在于设计的思想,开头也说过,其他框架的组件也有生命周期的实现,Spring与这些框架实现上有什么不同,这才是应该了解的;当然完全不了解其接口的作用也是不行的,这里强调的是不用刻意去死记硬背接口的含义, 在工作中多看看别人、别的框架的使用方式更有利于加深理解。

 

标签:生命周期,Spring,Object,bean,接口,Bean,throws
From: https://www.cnblogs.com/linyigg/p/16948294.html

相关文章

  • SpringCloud+MyBatis+Redis整合—— 超详细实例(一)
    1、SpringCloud+MyBatisMyBatis是一款优秀的轻量级半自动持久层框架,与之相对应的还有hibernate框架。①  话不多说,接下来搭建SpringCloud+MyBatis环境:第一步......
  • Spring Boot 单元测试笔记
    1.导入JUnit5测试框架<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope......
  • SpringCloud+MyBatis+Redis整合—— 超详细实例
    SpringCloud+MyBatis+Redisredis①是一种nosql数据库,以键值对<key,value>的形式存储数据,其速度相比于MySQL之类的数据库,相当于内存读写与硬盘读写的差别,所以常常用作缓存,用......
  • SpringCloud游戏平台改造-Day1
    Day1今天主要的工作是有,新建项目结构(后期可能会根据实际情况修改),实现了登录注册API项目思路目前的项目思路为以下几部分:GameAuth:用来提供用户登录注册接口,认证接口......
  • SpringCloud游戏平台改造-Day2
    Day2今天主要目的是接入SpringSecurity和JWT,不多说开干!Day1Day2接入SpringSecurityStep1实现来自SpringSecurity的UserDetailService接口,实现它的loaduserByUser......
  • 从doCreateBean看java对象的实例化和初始化
    下面是spring6.0源码中doCreateBean的部分源码/***Actuallycreatethespecifiedbean.Pre-creationprocessinghasalreadyhappened*atthispoint,e.g.c......
  • SpringCloud Alibaba(七) - JWT(JSON Web Token)
    原文链接:JWT详解:https://blog.csdn.net/weixin_45070175/article/details/1185592721、什么是JWT通俗地说,JWT的本质就是一个字符串,它是将用户信息保存到一个Json字符串......
  • Stemciljs学习2、组件生命周期
    组件有许多生命周期方法,可用于了解组件何时“将”和“执行”加载、更新和呈现。可以将这些方法添加到组件中,以便在正确的时间挂接到操作中。在组件类中实现以下方法之一,St......
  • BeanPostProcessor&BeanFactoryPostProcessor傻傻分不清
    最开始接触spring源码,被一堆的postProcessor搞晕了,翻译也不“包准”,有的翻译为【后置处理器】、有的翻译为【后置增强器】,这里姑且就翻译为处理器吧。众所周知,在spring框架......
  • hud:Being a Good Boy in Spring Festival(nim博弈方法数计算)
    ProblemDescription一年在外父母时刻牵挂春节回家你能做几天好孩子吗寒假里尝试做做下面的事情吧陪妈妈逛一次菜场悄悄给爸爸买个小礼物主动地强烈地要求洗一次碗......