SpringBoot初始化方法注解@PostConstruct和销毁前置方法注解@PreDestroy的实现源码

在一个Bean类中,被@PostConstruct注解标注的方法会被解析为该Bean的初始化方法调用的前置方法,在其初始化方法调用前调用
被@PreDestroy标注的方法会在Bean被销毁前调用

(上面这段话说错了,@PostConstruct标注的方法就是初始化方法,看到笔记最后,我如何发现我理解错了)

这种实现是由一个类来实现的,在Spring提供的几个扩展点中进行扩展:
该类为:InitDestroyAnnotationBeanPostProcessor

该类中有两个属性:

在该类初始化时,这两个属性会被赋予值:
initAnnotationType = PostConstruct.class  destroyAnnotationType = PreDestroy.class
即@PostConstruct 和 @PreDestroy 注解的class对象
用于后续判断方法上有没有这两个注解
这两个属性是该类的子类CommonAnnotationBeanPostProcessor初始化时在构造方法中被赋值的:

该子类还正巧是检测并处理自动装配注解@Resource的Spring自带的扩展类,参考笔记:
SpringBoot@Resource注入源码-CSDN博客
接着说本类:
还有一个内部类:

该内部类的两个属性,用于记录各个Bean的初始化前置方法和销毁前置方法

介绍完该类,说一下该类工作的全流程:
首先在容器刷新时AbstractApplicationContext的refresh方法中的registerBeanPostProcessors方法中被实例化并注入到单例池中,表明该类可供后续get其他Bean时候被调用,辅助扩展getBean操作

然后在Bean的获取过程中,即不管是Spring初始化单例池时或者我们自己调用时,都要调用getBean那一套逻辑,在该逻辑中:

调用到了本类的方法(本类就是实现类):

同样,与SpringBoot@Resource注入源码-CSDN博客SpringBoot的@Value和@AutoWired自动装配注解源码-CSDN博客那两个类一样的结构,也开一个缓存,缓存中没有就去buildLifecycleMetadata获取并放入到缓存中:

buildLifecycleMetadata方法:

然后调用这个:

OK,到此,这个扩展点执行完毕,完成的功能:每当getBean获取一个Bean时,在Bean实例化后遍历其方法,将有@PostConstruct和@PreDestroy标注的方法加入到本类的方法集合中,最终本类会存储到一堆Bean的初始化前置方法和销毁前置方法,以供后续调用使用

好,来到这些方法的调用阶段:
在getBean逻辑过程中最后一步initializeBean方法中:

主要看1方法:
调用接口BeanPostProcessor实现类的实现方法postProcessBeforeInitialization

会调用到本类,本类就是BeanPostProcessor实现类:

ok,结束

重大发现(为开篇的话和本文中的一些错误纠错):
看SpringBoot官方文档这段话
说的意思是,由CommonAnnotationBeanPostProcessor类(即本类InitDestroyAnnotationBeanPostProcessor的子类)实现的对@PostConstruct和@PreDestroy的扫描实现覆盖了原Spring的基于xml配置的初始化方法逻辑,即舍弃掉原Spring的那个初始化方法调用逻辑,将其扩展到SpringBoot基于注解的扫描处理逻辑中

在源码中的体现就是:

这张图,是原Spring的逻辑,没改动,原逻辑是2那部分是调用根据配置的初始化方法名字去调用Bean初始化方法的,但SpringBoot把该逻辑给置空了,等于这段逻辑啥都没干(如何置空在后面说),
然后将这段逻辑(调用Bean的初始化方法)给搬到(迁移)1那部分Spring留的扩展点中了,在该扩展点中调用
InitDestroyAnnotationBeanPostProcessor的方法,在其方法中会传入当前Bean的实例,并根据此实例找到本Bean的初始化方法并调用(这一套就是前面笔记所说的)。

接下来来看看SpringBoot如何把原Spring的2那部分逻辑置空:
我们进入到2那部分的方法中看看方法逻辑:
2的this.invokeInitMethods方法:

会从当前BeanDefinition拿InitMethodName的方法名字,判断不为空才去调用invokeCustomInitMethod
我们看看mbd.getInitMethod能拿到InitMethodName的方法名字不能:

发现直接返回BeanDefinition中的属性值,那得看看这个属性值在哪被设置的,给这个位置打上断点debug:

发现BeanDefinition的setInitMethodName在整个当前Bean的BeanDefinition创建过程中只会被调用一次,就是这里这次,是调用RootBeanDefinition的构造器中调用其抽象父类AbstractBeanDefinition的构造器中会调用setInitMethodName,传入的属性还是当前RootBeanDefinition中的initMethodName属性(originalAbd就是当前RootBeanDefinition实例对象),那么既然RootBeanDefinition的setInitMethodName在全过程中只有这一次被调用,而且还是RootBeanDefinition在被构建时(调用其构造器初始化)调用的,那证明RootBeanDefinition在整个创建过程中就没有别的地方会调用setInitMethodName为其设置值了,这唯一调用的一次还是自己把自己空的(null)initMethodName设置给抽象父类。
那到这已经明白了,SpringBoot就是这样不给Bean的BeanDefinition的initMethodName设置值,但仍保留这个属性(为了兼容Spring逻辑,Spring的原逻辑要用到这个属性值),但将其置空(null),导致Spring原逻辑拿不到该属性值,找不到要调用的初始化方法,实现置空了Spring原有的调用初始化方法的逻辑。
然后在1那个扩展点,将原Spring的该功能(调用初始化方法)迁移到SpringBoot自己的扩展实现中,秒啊。

所以我得把这个笔记的标题改一改了
初始化前置方法注解@PostConstruct --->(改为)  初始化方法注解@PostConstruct
@PostConstruct就是初始化方法的标注注解(正如其名post - 之后 Construct - 构造 在构造方法之后调用的方法)
那张图也得改成:

  • 27
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值