当你准备去复习Spring中Bean的生命周期的时候,这个时候你开始上网找资料,很大几率会看到下面这张图:程序员
先不论这张图上是否全面,可是就说这张图吧,你是否是背了又忘,忘了又背?web
究其缘由在于,你没有理解为何须要这些步骤,也不知道为何要按这个顺序执行微信
笔者在阅读完整个IOC
跟AOP
的源码后,但愿经过这篇文章讲一讲个人Spring中Bean生命周期的见解,帮助你们能理解性的记忆整个流程,而不是死记硬背!app
所谓理解也是创建在有必定知识储备的基础上的,因此这里先补充一些基础概念框架
Spring在建立一个Bean时是分为三个步骤的编辑器
@PostConstruct
注解)
Bean的生命周期指的就是在上面三个步骤中后置处理器BeanPostprocessor
穿插执行的过程ide
按照实现接口进行分类函数
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; } } 复制代码
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; } } 复制代码
可能有的小伙伴认为,第三种后置处理器确定就是用来在属性注入先后执行了的吧。我只能说,大兄弟,太天真了,看看下面这张图
这种状况下再为属性注入阶段专门提供两个方法是否是有点多余呢?实际上第三种后置处理器是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; } } 复制代码
通常咱们在探究生命周期的时候都不会考虑这种后置处理器的执行
在了解了上面的概念后,咱们再来看看这张图
至少如今这张图上缺乏了实例化先后后置处理器的执行流程,对吧?
再补充上这一点以后,咱们再来看看,属性注入后紧接着已是初始化的阶段,在初始化阶段开始前应该要调用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配置的方式
因此,这三个方法的执行顺序为:
@PostConstruct
注解标注的方法
InitializingBean
接口后复写的
afterPropertiesSet
方法
在完成初始化,没什么好说的了,最后调用一下postProcessAfterInitialization
,整个Bean的生命周期到此结束
本文的主要目的是想要帮助你们更好的理解整个Bean的生命周期,不过理解是创建在有必定知识存储的基础上的
你至少要对Bean的后置处理器跟Bean建立有一个大概的理解,那么经过本文你能理清一些细节方面的东西
例如,为何Aware接口执行在初始化阶段以前?为何初始化的三个方法会按
@PostConstruct
,afterPropertiesSet
,XML中定义的初始化方法这个顺序执行。
本文也将是我整个Spring关于IOC
跟AOP
的最后一篇文字,在这以后我打算作一个Spring事务专题,预计6到7篇文章,事务结束后关于整个Spring源码的学习也就结束啦!原本预期要一年才能完成,不过由于最近离职了,因此打算全职在家写完这个系列!
若是本文对你由帮助的话,记得点个赞吧!也欢迎关注个人公众号,微信搜索:程序员DMZ,或者扫描下方二维码,跟着我一块儿认认真真学Java,踏踏实实作一个coder。
我叫DMZ,一个在学习路上匍匐前行的小菜鸟!
本文使用 mdnice 排版