技术标签: spring java aop 编程语言 ioc
当你准备去复习Spring中Bean的生命周期的时候,这个时候你开始上网找资料,很大概率会看到下面这张图:
先不论这张图上是否全面,但是就说这张图吧,你是不是背了又忘,忘了又背?
究其原因在于,你没有理解为什么需要这些步骤,也不知道为什么要按这个顺序执行
笔者在阅读完整个IOC
跟AOP
的源码后,希望通过这篇文章讲一讲我的Spring中Bean生命周期的看法,帮助大家能理解性的记忆整个流程,而不是死记硬背!
所谓理解也是建立在有一定知识储备的基础上的,所以这里先补充一些基础概念
Spring在创建一个Bean时是分为三个步骤的
实例化,可以理解为new一个对象
属性注入,可以理解为调用setter方法完成属性注入
初始化,你可以按照Spring的规则配置一些初始化的方法(例如,@PostConstruct
注解)
Bean的生命周期指的就是在上面三个步骤中后置处理器BeanPostprocessor
穿插执行的过程
按照实现接口进行分类
直接实现了BeanPostProcessor
接口
最简单的后置处理器,也就是说直接实现了BeanPostProcessor
接口,这种后置处理器只能在初始化前后执行
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内部专用的后置处理器
可能有的小伙伴认为,第三种后置处理器肯定就是用来在属性注入前后执行了的吧。我只能说,大兄弟,太天真了,看看下面这张图
这种情况下再为属性注入阶段专门提供两个方法是不是有点多余呢?实际上第三种后置处理器是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
方法
XML中自定义的初始化方法
在完成初始化,没什么好说的了,最后调用一下postProcessAfterInitialization
,整个Bean的生命周期到此结束
本文的主要目的是想要帮助大家更好的理解整个Bean的生命周期,不过理解是建立在有一定知识存储的基础上的
你至少要对Bean的后置处理器跟Bean创建有一个大概的理解,那么通过本文你能理清一些细节方面的东西
例如,为什么Aware接口执行在初始化阶段之前?为什么初始化的三个方法会按
@PostConstruct
,afterPropertiesSet
,XML中定义的初始化方法这个顺序执行。
精彩推荐
一百期Java面试题汇总SpringBoot内容聚合IntelliJ IDEA内容聚合Mybatis内容聚合
欢迎长按下图关注公众号后端技术精选
题目链接题意有 \(n\) 个容积无限的水缸,初始时水量为\(a_1,a_2,...,a_n\),有一把容积为\(k\)的勺子,可以从一个水缸中舀水倒入另一个水缸中。问能否给出操作序列,使得最终某一个水缸中水的容量为\(V\).思路参考 - 粉兔.结论首先,如果\(\sum_{i=1}^{n}a_i\lt V\),显然不可行。否则,一旦\(\exists p_1,p_2,....
2019独角兽企业重金招聘Python工程师标准>>> ...
Python 格式化字符串漏洞(Django为例)在C语言里有一类特别有趣的漏洞,格式化字符串漏洞。轻则破坏内存,重则读写任意地址内容,二进制的内容我就不说了,说也不懂,分享个链接 https://github.com/shiyanlou/seedlab/blob/master/formatstring.mdPython中的格式化字符串Python中也有格式化字符串的方法,在Python2老版本中...
本文知识点:什么是 mini-batch 梯度下降mini-batch 梯度下降具体算法为什么需要 mini-batch 梯度下降batch, stochastic ,m...
有两个工程,繁体版本、简体版本,版本号不一样,如果需要同步里面的代码就需要用到第三方工具了。IDE中自带的svn功能较弱,还好有winMerge弥补了它的缺陷,它可以对比文件、文件夹,使用起来还是较为方便,界面也是中文。 “开始”菜单(快捷键ctrl+O),弹出对话框中选择需要进行对比的文件夹或文件(分为左侧、右侧) 然后选择一个过滤器,它自带就可以过滤掉svn目录,如需要过滤其它一些指...
相信大家在开发一些程序会有识别图片上文字(即所谓的OCR)的需求,比如识别车牌、识别图片格式的商品价格、识别图片格式的邮箱地址等等,当然需求最多的还是识别验证码。如果要完成这些OCR的工作,需要你掌握图像处理、图像识别的知识,需要用到图形形态学、傅里叶变换、矩阵变换、贝叶斯决策等很多复杂的理论,这让绝大部分人都会望而却步。 Tesseract这个开源项目的出现让我们普通人也可以涉足O
TCP如何提供可靠的数据传输(参考资料:TCP如何提供可靠传输)TCP的连接的建立是TCP进行可靠的数据传输的基础。后续在建立连接基础上,TCP又提供了如下技术保障数据的可靠的传输TCP根据MSS等数据,把需要传输的数据分割成TCP认为最合适的大小发送。TCP在交付了其报文给网络层后,将启动一个重传定时器,如果在该TCP报文的确认报文不能及时到达,将会启动重传。(差错控制,确认重传)当TCP数
1.java常量池的介绍java中的常量池,通常指的是运行时常量池,它是方法区的一部分,一个jvm实例只有一个运行常量池,各线程间共享该运行常量池。java常量池简介:java常量池中保存了一份在编译期间就已确定的数据。它里面包括final常量的值(包括成员常量、局部常量和引用常量)、以及对象字面量的值。在编译期间,每当给常量赋值它就会去检测常量池中是否存在该值,若存在直接返回该值的地址给常量,若...
1.读取csv模块import csv #导入csv模块class ReadCsv(): def read_csv(self): item =[] #定义一个空列表 c = csv.reader(open("../commonDemo/test1.csv","r")) #得到csv文件对象 for csv_i in c: item.append(csv_i) #将获取的数据添加到列表中
在开发android软件中,经常需要播放多媒体音频文件,通常会采用MediaPlayer类来执行此类操作。但MediaPlayer类占用的资源比较多,对于游戏等应用而言,会带来性能上的降低。在Android中,专门提供了SoundPool类来执行此类音频播放,SoundPool类占用的CPU资源较少、反应较快。SoundPool使用MediaPlaybackService将音频流解码为16
使用双分支条件语句编程,计算并输出两个整数的最大值。**输入格式要求:"%d,%d" 提示信息:"Input a, b:"**输出格式要求:"max = %d\n"程序运行示例如下:Input a, b:3,5max = 5#include<stdio.h>int main(){ int a, b; int max = 0; printf("Input a, b:"); scanf("%d,%d", &a, &b); if(a .
原标题:MIUI官方回应:米6公交开卡系服务升级、NFC功能可正常使用近日,有消息称小米6因为系统升级暂时关闭了NFC公交开卡服务,或涉及虚假宣传。对此,小米官方也给予了回应,以下为小米MIUI官方在论坛上的回应:NFC(近场通信)功能拥有三种工作模式:读卡器模式(Reader/Writer mode)、点对点模式(P2P mode)、卡模拟模式(Card emulation mode)。读卡器模...