1. 引言

“请你描述下 Spring Bean 的生命周期?”,这是面试官考察 Spring 的常用问题,可见是 Spring 中很重要的知识点。

我之前在准备面试时,去网上搜过答案,大多以下图给出的流程作为答案。

但是当我第一次看到该图时,就产生了很多困扰,“Aware,BeanPostProcessor......这些都是什么啊?而且这么多步骤,太多了,该怎么记啊?”。

其实要记忆该过程,还是需要我们先去理解,本文将从以下两方面去帮助理解 Bean 的生命周期:

  1. 生命周期的概要流程:对 Bean 的生命周期进行概括,并且结合代码来理解;
  2. 扩展点的作用:详细介绍 Bean 生命周期中所涉及到的扩展点的作用。

2. 生命周期的概要流程

Bean 的生命周期概括起来就是 4 个阶段

  1. 实例化(Instantiation)
  2. 属性赋值(Populate)
  3. 初始化(Initialization)
  4. 销毁(Destruction)

  1. 实例化:第 1 步,实例化一个 bean 对象;
  2. 属性赋值:第 2 步,为 bean 设置相关属性和依赖;
  3. 初始化:第 3~7 步,步骤较多,其中第 5、6 步为初始化操作,第 3、4 步为在初始化前执行,第 7 步在初始化后执行,该阶段结束,才能被用户使用;
  4. 销毁:第 8~10步,第8步不是真正意义上的销毁(还没使用呢),而是先在使用前注册了销毁的相关调用接口,为了后面第9、10步真正销毁 bean 时再执行相应的方法。

下面我们结合代码来直观的看下,在 doCreateBean() 方法中能看到依次执行了这 4 个阶段:

// AbstractAutowireCapableBeanFactory.java
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {// 1. 实例化BeanWrapper instanceWrapper = null;if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}Object exposedObject = bean;try {// 2. 属性赋值populateBean(beanName, mbd, instanceWrapper);// 3. 初始化exposedObject = initializeBean(beanName, exposedObject, mbd);}// 4. 销毁-注册回调接口try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}return exposedObject;
}

由于初始化包含了第 3~7步,较复杂,所以我们进到 initializeBean() 方法里具体看下其过程(注释的序号对应图中序号):

// AbstractAutowireCapableBeanFactory.java
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {// 3. 检查 Aware 相关接口并设置相关依赖if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {invokeAwareMethods(beanName, bean);return null;}, getAccessControlContext());}else {invokeAwareMethods(beanName, bean);}// 4. BeanPostProcessor 前置处理Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}// 5. 若实现 InitializingBean 接口,调用 afterPropertiesSet() 方法// 6. 若配置自定义的 init-method方法,则执行try {invokeInitMethods(beanName, wrappedBean, mbd);}catch (Throwable ex) {throw new BeanCreationException((mbd != null ? mbd.getResourceDescription() : null),beanName, "Invocation of init method failed", ex);}// 7. BeanPostProceesor 后置处理if (mbd == null || !mbd.isSynthetic()) {wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}return wrappedBean;
}

在 invokInitMethods() 方法中会检查 InitializingBean 接口和 init-method 方法,销毁的过程也与其类似:

// DisposableBeanAdapter.java
public void destroy() {// 9. 若实现 DisposableBean 接口,则执行 destory()方法if (this.invokeDisposableBean) {try {if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {((DisposableBean) this.bean).destroy();return null;}, this.acc);}else {((DisposableBean) this.bean).destroy();}}}// 10. 若配置自定义的 detory-method 方法,则执行if (this.destroyMethod != null) {invokeCustomDestroyMethod(this.destroyMethod);}else if (this.destroyMethodName != null) {Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);if (methodToInvoke != null) {invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));}}

从 Spring 的源码我们可以直观的看到其执行过程,而我们记忆其过程便可以从这 4 个阶段出发,实例化、属性赋值、初始化、销毁。其中细节较多的便是初始化,涉及了 Aware、BeanPostProcessor、InitializingBean、init-method 的概念。这些都是 Spring 提供的扩展点,其具体作用将在下一节讲述。

3. 扩展点的作用

3.1 Aware 接口

若 Spring 检测到 bean 实现了 Aware 接口,则会为其注入相应的依赖。所以通过让bean 实现 Aware 接口,则能在 bean 中获得相应的 Spring 容器资源

Spring 中提供的 Aware 接口有:

  1. BeanNameAware:注入当前 bean 对应 beanName;
  2. BeanClassLoaderAware:注入加载当前 bean 的 ClassLoader;
  3. BeanFactoryAware:注入 当前BeanFactory容器 的引用。

其代码实现如下:

// AbstractAutowireCapableBeanFactory.java
private void invokeAwareMethods(final String beanName, final Object bean) {if (bean instanceof Aware) {if (bean instanceof BeanNameAware) {((BeanNameAware) bean).setBeanName(beanName);}if (bean instanceof BeanClassLoaderAware) {((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);}if (bean instanceof BeanFactoryAware) {((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);}}
}

以上是针对 BeanFactory 类型的容器,而对于 ApplicationContext 类型的容器,也提供了 Aware 接口,只不过这些 Aware 接口的注入实现,是通过 BeanPostProcessor 的方式注入的,但其作用仍是注入依赖。

  1. EnvironmentAware:注入 Enviroment,一般用于获取配置属性;
  2. EmbeddedValueResolverAware:注入 EmbeddedValueResolver(Spring EL解析器),一般用于参数解析;
  3. ApplicationContextAware(ResourceLoader、ApplicationEventPublisherAware、MessageSourceAware):注入 ApplicationContext 容器本身。

其代码实现如下:

// ApplicationContextAwareProcessor.java
private void invokeAwareInterfaces(Object bean) {if (bean instanceof EnvironmentAware) {((EnvironmentAware)bean).setEnvironment(this.applicationContext.getEnvironment());}if (bean instanceof EmbeddedValueResolverAware) {((EmbeddedValueResolverAware)bean).setEmbeddedValueResolver(this.embeddedValueResolver);}if (bean instanceof ResourceLoaderAware) {((ResourceLoaderAware)bean).setResourceLoader(this.applicationContext);}if (bean instanceof ApplicationEventPublisherAware) {((ApplicationEventPublisherAware)bean).setApplicationEventPublisher(this.applicationContext);}if (bean instanceof MessageSourceAware) {((MessageSourceAware)bean).setMessageSource(this.applicationContext);}if (bean instanceof ApplicationContextAware) {((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);}}

3.2 BeanPostProcessor

BeanPostProcessor 是 Spring 为**修改 bean **提供的强大扩展点,其可作用于容器中所有 bean,其定义如下:

public interface BeanPostProcessor {// 初始化前置处理default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}// 初始化后置处理default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}}

常用场景有:

  1. 对于标记接口的实现类,进行自定义处理。例如3.1节中所说的ApplicationContextAwareProcessor,为其注入相应依赖;再举个例子,自定义对实现解密接口的类,将对其属性进行解密处理;
  2. 为当前对象提供代理实现。例如 Spring AOP 功能,生成对象的代理类,然后返回。
// AbstractAutoProxyCreator.java
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {TargetSource targetSource = getCustomTargetSource(beanClass, beanName);if (targetSource != null) {if (StringUtils.hasLength(beanName)) {this.targetSourcedBeans.add(beanName);}Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);this.proxyTypes.put(cacheKey, proxy.getClass());// 返回代理类return proxy;}return null;
}

3.3 InitializingBean 和 init-method

InitializingBean 和 init-method 是 Spring 为 bean 初始化提供的扩展点。

InitializingBean接口 的定义如下:

public interface InitializingBean {void afterPropertiesSet() throws Exception;
}

在 afterPropertiesSet() 方法写初始化逻辑。

指定 init-method 方法,指定初始化方法:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="demo" class="com.chaycao.Demo" init-method="init()"/></beans>

DisposableBean 和 destory-method 与上述类似,就不描述了。

4. 总结

最后总结下如何记忆 Spring Bean 的生命周期:

  • 首先是实例化、属性赋值、初始化、销毁这 4 个大阶段;
  • 再是初始化的具体操作,有 Aware 接口的依赖注入、BeanPostProcessor 在初始化前后的处理以及 InitializingBean 和 init-method 的初始化操作;
  • 销毁的具体操作,有注册相关销毁回调接口,最后通过DisposableBean 和 destory-method 进行销毁。

字节跳动面试题:“请你描述下 Spring Bean 的生命周期?”相关推荐

  1. 描述一下Spring Bean 的生命周期

  2. 请描述下你对 Vue 生命周期的理解?在 created 和 mounted 这两个生命周期中请求数据有什么区别呢?

    Vue 生命周期总共可以分为8个阶段:初始化前后,挂载前后,更新前后,销毁前后,以及一些特殊场景的生命周期. 一.基本生命周期 生命周期 描述 beforeCreate 组件实例被创建之初 creat ...

  3. 请解释Spring Bean 的生命周期?

    Spring Bean 的生命周期简单易懂.在一个bean 实例被初始化时,需要执行一系列的初始化操作以达到可用的状态.同样的,当一个bean 不在被调用时需要进行相关的析构操作,并从bean 容器中 ...

  4. Spring Bean的生命周期面试题,来自抖音 :文艺TOM

    (这个文章根据抖音的文艺TOM博主抄的,觉得挺好,可以背背,当成面试用,另外我加了一点点) Spring的生命周期被分为五个阶段: 创建前准备阶段 创建实例阶段 依赖注入阶段 容器缓存阶段 销毁实例阶 ...

  5. 字节跳动java笔试题目_牛客网--字节跳动面试题--特征提取

    牛客网--字节跳动面试题--特征提取 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 来源 链接:特征提取 来源:牛客网 题目 ...

  6. 牛客网--字节跳动面试题--雀魂启动

    牛客网--字节跳动面试题--雀魂启动 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 来源 链接:雀魂启动 来源:牛客网 题目 ...

  7. 最新字节跳动面试题与岗位层级,绩效考核制度介绍

    最新字节跳动面试题与岗位层级,绩效考核制度介绍 1.算法题一:无序数组的中位数 (快排思想O(N) 时间复杂度) 2.算法题二:给一数组,让你找一对满足i 3.算法题三: 给一数组,让你找一对满足i& ...

  8. 最新字节跳动面试题与答案: 无序数组的中位数 (快排思想O(N) 时间复杂度)

    最新字节跳动面试题与答案 1.算法题一:无序数组的中位数 (快排思想O(N) 时间复杂度) package com.lightsword.leetcodeproblemsimport org.juni ...

  9. 编程实践系列: 字节跳动面试题

    字节跳动面试题 面试3轮技术面+1轮HR 整体分三大块: 项目经验 计算机基础 编程题(每轮一题) 项目经验 架构+业务 做了什么核心设计优化 解决了什么难题 体现思考 业务理解 架构设计能力 项目需 ...

最新文章

  1. love2d--glsl03噪声
  2. IDE:集成开发环境(Integrated Development Environment)
  3. 如何在阅读参考文献时,有效率的做笔记?
  4. java 调用dll内存泄露_对 精致码农大佬 说的 Task.Run 会存在 内存泄漏 的思考
  5. 吴恩达机器学习作业 6.支持向量机
  6. Qualcomm 推出下一代物联网专用蜂窝技术芯片组!
  7. [转载] python中的type和object详解
  8. C语言静态链表常用吗,C语言实现静态链表
  9. Java 嵌入数据处理引擎:SQLite 的挑战者
  10. WireShark基本使用(7)第 7 章 高级
  11. spring boot整合微信支付
  12. 魔术表演的核心秘密(一)——开篇简介
  13. 一个快速视频剪辑编辑神器 — Moviepy
  14. 戴尔服务器r510怎么系统,DELLR510服务器上安系统.docx
  15. 我国建成世界第一超级计算机,国防科大研制出超级计算机 运算性能世界第一...
  16. webrtc jitter buffer
  17. 路由器恢复出厂设置,如何重新配置访问网络
  18. Microsoft .NET Framework 3.5 sp1离线安装(DotNetFX35)
  19. 实例演示R语言制作限制性立方条图
  20. 使用 iframe sandbox 时的注意点

热门文章

  1. 【OpenCV3】棋盘格角点检测与绘制——cv::findChessboardCorners()与cv::drawChessboardCorners()详解
  2. nagios监控mysql主从复制
  3. Sitecore7.5 安装指南 -- 从.EXE文件安装Sitecore CMS
  4. Repository模式(转载)
  5. 敏捷结果30天之第四天:放弃一些事情
  6. MySQL中各种字段的取值范围
  7. 网络投票轻松实现自动化 - 验证码识别的基本思路及方法
  8. C语言socket设置超时的几种方法
  9. 数据降维(PCA、KPCA、PPCA)及C++实现
  10. YOLOX——Win10下训练自定义VOC数据集