若是你每次面试前都要去背一篇Spring中Bean的生命周期,请看完这篇文章

前言

当你准备去复习Spring中Bean的生命周期的时候,这个时候你开始上网找资料,很大几率会看到下面这张图:程序员

image-20200707094637369
image-20200707094637369

先不论这张图上是否全面,可是就说这张图吧,你是否是背了又忘,忘了又背?web

究其缘由在于,你没有理解为何须要这些步骤,也不知道为何要按这个顺序执行微信

笔者在阅读完整个IOCAOP的源码后,但愿经过这篇文章讲一讲个人Spring中Bean生命周期的见解,帮助你们能理解性的记忆整个流程,而不是死记硬背!app

基础知识补充

所谓理解也是创建在有必定知识储备的基础上的,因此这里先补充一些基础概念框架

Bean建立的三个阶段

Spring在建立一个Bean时是分为三个步骤的编辑器

  • 实例化,能够理解为new一个对象
  • 属性注入,能够理解为调用setter方法完成属性注入
  • 初始化,你能够按照Spring的规则配置一些初始化的方法(例如, @PostConstruct注解)

生命周期的概念

Bean的生命周期指的就是在上面三个步骤中后置处理器BeanPostprocessor穿插执行的过程ide

后置处理器的分析

按照实现接口进行分类函数

  1. 直接实现了 BeanPostProcessor接口

最简单的后置处理器,也就是说直接实现了BeanPostProcessor接口,这种后置处理器只能在初始化先后执行post

public interface BeanPostProcessor {
  // 初始化前执行的方法  @Nullable  default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  return bean;  }   // 初始化后执行的方法  default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {  return bean;  }  } 复制代码
  1. 直接实现了 InstantiationAwareBeanPostProcessor接口

在第一种后置处理的基础上进行了一层扩展,能够在Bean的实例化阶段先后执行学习

// 继承了BeanPostProcessor,额外提供了两个方法用于在实例化先后的阶段执行
// 由于实例化后紧接着就要进行属性注入,因此这个接口中还提供了一个属性注入的方法 public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {   // 实例化前执行  default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {  return null;  }   // 实例化后置  default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {  return true;  }   // 属性注入  default PropertyValues postProcessPropertyValues(  PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {   return pvs;  } } 复制代码
  1. Spring内部专用的后置处理器

可能有的小伙伴认为,第三种后置处理器确定就是用来在属性注入先后执行了的吧。我只能说,大兄弟,太天真了,看看下面这张图

image-20200707163736980
image-20200707163736980

这种状况下再为属性注入阶段专门提供两个方法是否是有点多余呢?实际上第三种后置处理器是Spring为了本身使用而专门设计的

public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {
  // 推测bean的类型,例如在属性注入阶段咱们就须要知道符合依赖类型的Bean有哪些  @Nullable  default Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {  return null;  }   // 推断出全部符合要求的构造函数,在实例化对象的时候咱们就须要明确到底使用哪一个构造函数  @Nullable  default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)  throws BeansException {   return null;  }   // 获取一个提早暴露的对象,用于解决循环依赖  default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {  return bean;  }  } 复制代码

通常咱们在探究生命周期的时候都不会考虑这种后置处理器的执行

生命周期详细介绍

在了解了上面的概念后,咱们再来看看这张图

image-20200707094637369
image-20200707094637369

至少如今这张图上缺乏了实例化先后后置处理器的执行流程,对吧?

再补充上这一点以后,咱们再来看看,属性注入后紧接着已是初始化的阶段,在初始化阶段开始前应该要调用BeanPostProcessor的预初始化方法(postProcessBeforeInitialization),而后调用自定义的初始化方法,最后调用postProcessAfterInitialization,这是没有问题,可是为何要在初始前还要调用Aware接口的方法,若是你看了源码的话可能会说,源码就是这么写的,别人就是这么设计的,可是为何要这么设计呢?咱们看源码到必定阶段后不该该仅仅停留在是什么的阶段,而应该多思考为何,这样能帮助你更好的了解这个框架

那么为何Aware接口非要在初始化前执行呢?

这样作的目的是由于,初始化可能会依赖Aware接口提供的状态,例以下面这个例子

@Component
public class A implements InitializingBean, ApplicationContextAware {   ApplicationContext applicationContext;   @Override  public void afterPropertiesSet() throws Exception {  // 初始化方法须要用到ApplicationContextAware提供的ApplicationContext  System.out.println(applicationContext);  }   @Override  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {  this.applicationContext = applicationContext;  } } 复制代码

这种状况下Aware接口固然要在初始化前执行啦!

另外,在讨论Bean的初始化的时候常常会碰到下面这个问题,@PostConstruct,afterPropertiesSet跟XML中配置的init-method方法的执行顺序。

请注意,@PostConstruct其实是在postProcessBeforeInitialization方法中处理的,严格来讲它不属于初始化阶段调用的方法,因此这个方法是最早调用的

其次咱们思考下是调用afterPropertiesSet方法的开销大仍是执行配置文件中指定名称的初始化方法开销大呢?咱们不妨用伪代码演示下

// afterPropertiesSet,强转后直接调用
((InitializingBean) bean).afterPropertiesSet()  // 反射调用init-method方法 // 第一步:找到这个方法 Method method = class.getMethod(methodName) // 第二步:反射调用这个方法 method.invoke(bean,null) 复制代码

相比而言确定是第一种的效率高于第二种,一个只是作了一次方法调用,而另一个要调用两次反射。

所以,afterPropertiesSet的优先级高于XML配置的方式

因此,这三个方法的执行顺序为:

  1. @PostConstruct注解标注的方法
  2. 实现了 InitializingBean接口后复写的 afterPropertiesSet方法
  3. XML中自定义的初始化方法

在完成初始化,没什么好说的了,最后调用一下postProcessAfterInitialization,整个Bean的生命周期到此结束

总结

本文的主要目的是想要帮助你们更好的理解整个Bean的生命周期,不过理解是创建在有必定知识存储的基础上的

你至少要对Bean的后置处理器跟Bean建立有一个大概的理解,那么经过本文你能理清一些细节方面的东西

例如,为何Aware接口执行在初始化阶段以前?为何初始化的三个方法会按

@PostConstructafterPropertiesSet,XML中定义的初始化方法这个顺序执行。

本文也将是我整个Spring关于IOCAOP的最后一篇文字,在这以后我打算作一个Spring事务专题,预计6到7篇文章,事务结束后关于整个Spring源码的学习也就结束啦!原本预期要一年才能完成,不过由于最近离职了,因此打算全职在家写完这个系列!

若是本文对你由帮助的话,记得点个赞吧!也欢迎关注个人公众号,微信搜索:程序员DMZ,或者扫描下方二维码,跟着我一块儿认认真真学Java,踏踏实实作一个coder。

公众号
公众号

我叫DMZ,一个在学习路上匍匐前行的小菜鸟!

本文使用 mdnice 排版

相关文章
相关标签/搜索