前言

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

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

究其原因在于,你没有理解为什么需要这些步骤,也不知道为什么要按这个顺序执行

笔者在阅读完整个IOCAOP的源码后,希望通过这篇文章讲一讲我的Spring中Bean生命周期的看法,帮助大家能理解性的记忆整个流程,而不是死记硬背!


基础知识补充

所谓理解也是建立在有一定知识储备的基础上的,所以这里先补充一些基础概念

Bean创建的三个阶段

Spring在创建一个Bean时是分为三个步骤的

  • 实例化,可以理解为new一个对象

  • 属性注入,可以理解为调用setter方法完成属性注入

  • 初始化,你可以按照Spring的规则配置一些初始化的方法(例如,@PostConstruct注解)

生命周期的概念

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

后置处理器的分析

按照实现接口进行分类

  1. 直接实现了BeanPostProcessor接口

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

public interface BeanPostProcessor {// 初始化前执行的方法@Nullabledefault 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内部专用的后置处理器

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

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

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

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

生命周期详细介绍

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

至少现在这张图上缺少了实例化前后后置处理器的执行流程,对吧?

再补充上这一点之后,我们再来看看,属性注入后紧接着已经是初始化的阶段,在初始化阶段开始前应该要调用BeanPostProcessor的预初始化方法(postProcessBeforeInitialization),然后调用自定义的初始化方法,最后调用postProcessAfterInitialization

这是没有问题,但是为什么要在初始前还要调用Aware接口的方法,如果你看了源码的话可能会说,源码就是这么写的,别人就是这么设计的,但是为什么要这么设计呢?我们看源码到一定阶段后不应该仅仅停留在是什么的阶段,而应该多思考为什么,这样能帮助你更好的了解这个框架

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

这样做的目的是因为,初始化可能会依赖Aware接口提供的状态,例如下面这个例子

@Component
public class A implements InitializingBean, ApplicationContextAware {ApplicationContext applicationContext;@Overridepublic void afterPropertiesSet() throws Exception {// 初始化方法需要用到ApplicationContextAware提供的ApplicationContextSystem.out.println(applicationContext);}@Overridepublic 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中定义的初始化方法这个顺序执行。

有道无术,术可成;有术无道,止于术

欢迎大家关注Java之道公众号

好文章,我在看❤️

如果你每次面试前都要去背一篇Spring中Bean的生命周期,请看完这篇文章相关推荐

  1. 面试问题:Spring中Bean 的生命周期

    Spring Bean生命周期执行流程 在传统的 Java 应用中,Bean 的生命周期很简单,使用关键字 new 实例化 Bean,当不需要该 Bean 时,由 Java 自动进行垃圾回收. Spr ...

  2. 图解 Java 线程的生命周期,看完再也不怕面试官问了

    文章首发自个人微信公众号: 小哈学Java www.exception.site/java-concur- 在 Java 初中级面试中,关于线程的生命周期可以说是常客了.本文就针对这个问题,通过图文并 ...

  3. 再见面试官:你能说说 Spring 框架中 Bean 的生命周期吗?

    首先简单说一下(以下为一个回答的参考模板) 1.实例化一个Bean--也就是我们常说的new: 2.按照Spring上下文对实例化的Bean进行配置--也就是IOC注入: 3.如果这个Bean已经实现 ...

  4. Spring第三天,详解Bean的生命周期,学会后让面试官无话可说!

    点击下方链接回顾往期 不要再说不会Spring了!Spring第一天,学会进大厂! Spring第二天,你必须知道容器注册组件的几种方式!学废它吊打面试官! 今天讲解Spring中Bean的生命周期. ...

  5. 程序员面试前都要做哪些准备

    程序员面试前都要做哪些准备呢,老师带你了解下: 1.简历准备 简历时每个行业面试前都需要准备的,一份适合的简历会给面试带来一个好的开始.简历一定要符合自己情况,真实填写.程序员除了常规的基本信息.学历 ...

  6. hashmap是有序还是无序_说实话,你要是看完这篇 HashMap ,和面试官扯皮真的就没问题了!

    文章来源:看完这篇 HashMap ,和面试官扯皮就没问题了 原文作者:cxuan 来源平台:微信公众号 (如果你没有时间细抠本文,可以直接看 HashMap 概述,能让你对 HashMap 有个大致 ...

  7. redis hashmap过期_看完这篇再也不怕 Redis 面试了

    0.前言 Redis是跨语言的共同技术点,无论是Java还是C++都会问到,所以是个高频面试点. 笔者是2017年才开始接触Redis的,期间自己搭过单机版和集群版,不过现在 大一些的 公司都完全是运 ...

  8. 看完这篇再也不怕 Redis 面试了

    看完这篇再也不怕 Redis 面试了 0x00.前言 Redis是跨语言的共同技术点,无论是Java还是C++都会问到,所以是个高频面试点. 笔者是2017年才开始接触Redis的,期间自己搭过单机版 ...

  9. 看完这篇,我再也不怕面试官问垃圾收集了

    看完这篇,我再也不怕面试官问垃圾收集了 说在前面:本文的篇幅较长,看本文的时候最好先去上个厕所,先准备好一杯枸杞茶,慢慢品,本文将会讲解三种垃圾收集算法:标记-清除.复制.标记-整理算法,以及各种成熟 ...

最新文章

  1. 从配置文件到分布式配置管理QConf
  2. 如何自学python爬虫-Python爬虫:零基础该如何学习爬虫
  3. 【Android 逆向】修改 Android 的 apk 安装包内的文件并重新打包 ( apktool_2.6.0.jar 下载和使用 | zipalign 文件对齐 | apksigner 签名 )
  4. 在电影里看到的一个小故事
  5. 目前主流的四大浏览器内核Trident、Gecko、WebKit以及Presto
  6. python dict初始化大小_在Python中初始化/创建/填充Dict的Dict
  7. c+oracle+bulk,C#使用OracleBulkCopy
  8. json、js数组真心不是想得那么简单
  9. c++ primer练习题 第七章 类 (Class)
  10. Android Studio开发学习 - 1. 添加Activity
  11. No module named swigfaiss
  12. java语言实现_java语言实现树
  13. Flex 学习随笔 --- 找学习资料+安装环境+工具
  14. 写了个算分压电阻阻值的MATLAB小程序
  15. udacity 学java_Udacity前端开发(入门)第一个月学习小结
  16. Ubuntu上安装博通无线网卡驱动
  17. win2012服务器 注册表,第十一章 Windows Server 2012 R2 注册表域注册表编辑器 ---学习笔记...
  18. 应用之星VS AppCan——对比两大移动开发平台
  19. web前端程序员到底值多少钱?
  20. B15 - 999、大数据组件学习⑫ - Hue

热门文章

  1. 魅蓝2 android 8,魅蓝E2和魅蓝2哪个好
  2. php公众号客服系统,公众号在线客服系统哪个好,主流客服系统评测及推荐
  3. java中截取部分字符串_JAVA中截取字符串substring用法详解
  4. 虚拟机机操作系统已禁用 cpu。请关闭或重置虚拟机。_黑科技教学丨Win10竟然内置了一台虚拟机?教你如何玩转它...
  5. 软件工程模块开发卷宗_数据科学家应该了解的软件工程实践
  6. linux文件系统 环形结构图,环形缓冲器(转)
  7. matlab模拟简单孔径衍射图样,夫琅和费衍射实验文献综述
  8. dnf加物理攻击的卡片有哪些_DNF:节日宝珠之外百分比神器附魔,拍卖行100w,实用不氪金...
  9. SetWindowsHookEx
  10. C++中的深拷贝和浅拷贝(详解)