大家好,我是栗子为。
“最近为毕业材料的事情忙得焦头烂额,也有几天没复习知识点了,在写本文的内容之前,小为想和大家分享最近听到的一个词——钝感力,我们每天收到的讯息太多了,在我们还无法判断这些讯息带来的影响前,保持这份钝感力,从容面对生活中的各种事情,这才是通往幸福生活的关键。”
回归正题,今天想和大家聊聊在面试中,框架部分问得比较多的一个问题,那就是bean的生命周期。
小为之前准备面试的过程中也是查阅了很多资料,剖析源码、拆分每一个过程,觉得这一部分还是挺重要的,当在面试中能很流畅地介绍bean的生命周期,面试官一定会觉得你有很强的阅读源码的能力,你也因此能从面试中脱颖而出。
小为这就带着大家,来攻破这一难题…
01
—
什么是spring bean
02
—
bean的作用域有哪些?
-
singleton::spring中默认的作用域,即以单例方式存在 -
prototype:对每次从容器中调用bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean() -
request:对于一次HTTP请求,新建一个bean实例,该作用域仅适用于web的Spring WebApplicationContext环境 -
session:同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。 -
global-session:全局session作用域,简单举个🌰,负载均衡会把请求分发到不同的服务器上,为了携带请求参数,利用全局的session来存储,便于服务器验证
03
—
bean的生命周期
bean的生命周期可以简单概括为:
实例化->属性填充->初始化->销毁
下面看具体每个过程:
实例化
doCreateBean
方法,我们可以看到首先需要实例化这个bean,也就是在堆中开辟一块内存空间给这个对象,createBeanInstance
方法里面逻辑大概就是采用反射生成实例对象,进行到这里表示对象还并未进行属性的填充,也就是@Autowired注解的属性还未得到注入。
属性填充
populateBean
方法里面的逻辑大致就是对使用到了注入属性的注解就会进行注入,如果在注入的过程发现注入的对象还没生成,则会跑去生产要注入的对象
初始化
调用 invokeAwareMethods
方法
如果我们实现了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware三个Aware接口的话,会依次调用setBeanName(), setBeanClassLoader(), setBeanFactory()方法 调用 applyBeanPostProcessorsBeforeInitialization
方法
如果多个类实现BeanPostProcessor接口,那么多个实现类都会执行postProcessBeforeInitialization方法,阅读源码可以看到是for循环依次执行的,还有一个注意的点就是如果加载A类到spring容器中,A类也重写了BeanPostProcessor接口的postProcessBeforeInitialization方法,这时要注意A类的postProcessBeforeInitialization方法并不会得到执行,因为A类还未加载完成,还未完全放到spring的singletonObjects一级缓存中。 调用 invokeInitMethods
方法
发现如果实现了InitializingBean接口,重写了afterPropertiesSet方法,则会调用afterPropertiesSet方法,最后还会调用是否指定了init-method,可以通过 标签,或者@Bean注解的initMethod指定 调用 applyBeanPostProcessorsAfterInitialization
方法
循环遍历实现了BeanPostProcessor的接口实现类,执行postProcessAfterInitialization方法。
销毁
调用DisposableBean的 destory
方法
如果上面的文字不方便去记忆,小为也给大家整理一张bean生命周期的流程图,请接着往下看

小为也是总结了一套流程方便大家记忆,当然大家一定要在理解的基础上去记忆,在面试中能有更好的表现
-
创建前的准备阶段
实例化BeanFactory后置处理器(BeanFactoryPostProcessor) -> 实例化Bean后置处理器(BeanPostProcessor) -> 实例化BeanPostProcessor实现类 -> 实例化感知的Bean后置处理器 -> 在实例化之前的后置处理
总结:这一阶段主要从Spring上下文和配置信息中解析和查找和bean有关的配置,以及BeanFactoryPostProcessor这类bean加载过程的前置和后置处理
-
创建实例阶段
通过反射来创建bean的实例对象,并扫描和解析bean声明的属性
-
依赖注入阶段
调用BeanNameAware的setBeanName方法 -> 调用BeanFactoryAware的setBeanFactory方法 -> 执行BeanPostProcessor的postProcessBeforeInitialization方法(后置处理器在实例化之前的方法) -> 调用InitializingBean的afterPropertieSet方法(设置好所有的Bean属性之后,由BeanFactory调用)
总结:在这个阶段,如果实例化的Bean依赖其他bean,就需要对这些bean进行注入,在这个阶段还会触发一些扩展的调用,比如BeanPostProcessor(用来实现Bean初始化前后的回调)、InitializingBean类(可以给属性复制)以及BeanFactoryAware等
-
容器缓存阶段
调用Bean的init-method方法(在配置中属性指定的方法) -> 执行BeanPostProcessor的postProcessAfterInitialization方法(Bean后置处理器在实例化之后执行) -> 执行InstantiationAwareBeanPostProcessor的postProcessAfterInitialization方法(初始化感知后置处理器在实例化之后执行)
总结:这个阶段的bean由于被缓存到IoC容器中,所以可以被开发者使用了,这一阶段会调用init-method这个属性配置的方法,或后置处理器方法,如postProcessAfterInitialization
-
销毁实例阶段
调用DisposableBean的destory方法 -> 调用Bean的destory-method方法(在配置中属性指定的方法)
总结:这个阶段是当Spring应用上下文关闭时,销毁Spring上下文中的所有Bean,如果bean实现了DisposableBean接口,或者配置了destory-method属性,将会在这个阶段被调用
关于bean生命周期的内容就是以上这些了,上述流程图中小为留下了一个伏笔,就是关于spring中三级缓存的问题,小为之前在面试中也被问到了“你知道循环依赖吗?Spring如何解决循环依赖问题?”
当时正好阅读过源码,所以小为在面试中还是轻松拿下了这个问题,在这里分享给大家
04
—
三级缓存解决循环依赖的流程
循环依赖最简单的🌰就是,A依赖于B,B依赖于A
-
A首先进行实例化,通过ObjectFactory半成品暴露在三级缓存中 -
填充B的属性,发现B还未进行过加载,就会先去加载B -
实例化B,也通过ObjectFactory半成品暴露在三级缓存中 -
给B填充属性A的时候,发现可以从三级缓存中拿到半成品A的ObjectFactory -
拿到ObjectFactory对象后,调用其 getObject()
方法最终会调用getEarlyBeanReference()
方法,其逻辑是,如果bean被AOP切面代理则返回的是beanProxy对象,如果未被代理则返回的是原bean实例,但此时拿到的bean实例属性还未填充 -
从三级缓存中移除,放到二级缓存earlySingletonObjects中,此时B注入的是半成品实例A对象,随着B初始化完成,A会进行后续初始化操作,最终B会注入一个完整的A实例
问:感觉二级缓存有点多余?
答:只要两个缓存确实可以做到解决循环依赖的问题,但是有一个前提这个bean没被AOP进行切面代理,如果这个bean被AOP进行了切面代理,那么只使用两个缓存是无法解决问题
解释:我们每执行一遍singleFactory.getObject()方法就会产生一个新的代理对象,又因为A是单例的,所以借助二级缓存来解决,将产生的实例对象放到二级缓存中,后面初始化从二级缓存中拿,保证只有一个代理对象

-
singletonObjects:第一级缓存,里面放置的是已经实例化好的单例对象; -
earlySingletonObjects:第二级缓存,里面存放的是提前曝光的单例对象; -
singletonFactories:第三级缓存,里面存放的是将要被实例化的对象的对象工厂。
05
—
总结
以上就是今天的全部内容啦,介绍了bean的作用域,生命周期以及三级循环解决循环依赖问题,在面试中被问到这些,我相信认真看完这篇还是能和面试官好好聊上几回合。那我们下次再继续聊~
关注六只栗子,面试不迷路
作者 栗子为
编辑 一口栗子
原文始发于微信公众号(六只栗子):一文搞懂bean的生命周期
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/88666.html