写在前面

在前面的《【String注解驱动开发】你真的了解@PostConstruct注解和@PreDestroy注解吗?》一文中,我们简单的介绍了@PostConstruct注解与@PreDestroy注解的用法,有不少小伙伴纷纷留言说:在Spring中,@PostConstruct注解与@PreDestroy注解标注的方法是在哪里调用的呀?相信大家应该都挺好奇的吧,那今天我们就来一起分析下@PostConstruct注解与@PreDestroy注解的执行过程吧!

项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation

注解说明

@PostConstruct,@PreDestroy是Java规范JSR-250引入的注解,定义了对象的创建和销毁工作,同一期规范中还有注解@Resource,Spring也支持了这些注解。

在Spring中,@PostConstruct,@PreDestroy注解的解析是通过BeanPostProcessor实现的,具体的解析类是org.springframework.context.annotation.CommonAnnotationBeanPostProcessor,其父类是org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor,Spring官方说明了该类对JSR-250中@PostConstruct,@PreDestroy,@Resource注解的支持。

Spring's org.springframework.context.annotation.CommonAnnotationBeanPostProcessor supports the JSR-250 javax.annotation.PostConstruct and javax.annotation.PreDestroy annotations out of the box, as init annotation and destroy annotation, respectively. Furthermore, it also supports the javax.annotation.Resource annotation for annotation-driven injection of named beans.

调用过程

具体过程是,IOC容器先解析各个组件的定义信息,解析到@PostConstruct,@PreDestroy的时候,定义为生命周期相关的方法,组装组件的定义信息等待初始化;在创建组件时,创建组件并且属性赋值完成之后,在执行各类初始化方法之前,从容器中找出所有BeanPostProcessor的实现类,其中包括InitDestroyAnnotationBeanPostProcessor,执行所有BeanPostProcessor的postProcessBeforeInitialization方法,在InitDestroyAnnotationBeanPostProcessor中就是找出被@PostConstruct修饰的方法的定义信息,并执行被@PostConstruct标记的方法。

调用分析

@PostConstruct的调用链如下:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(String, Object, RootBeanDefinition)初始化流程中,先执行org.springframework.beans.factory.config.BeanPostProcessor.postProcessBeforeInitialization(Object, String)方法,然后再执行初始化方法:

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
invokeAwareMethods(beanName, bean);
return null;
}
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
} Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 在执行初始化方法之前:先执行org.springframework.beans.factory.config.BeanPostProcessor.postProcessBeforeInitialization(Object, String)方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
} try {
//执行InitializingBean的初始化方法和init-method指定的初始化方法
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
} if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}

org.springframework.beans.factory.config.BeanPostProcessor.postProcessBeforeInitialization(Object, String)的说明如下:

Apply this BeanPostProcessor to the given new bean instance before any bean initialization callbacks (like InitializingBean's afterPropertiesSet or a custom init-method). The bean will already be populated with property values. The returned bean instance may be a wrapper around the original.

调用时机: 在组件创建完属性复制完成之后,调用组件初始化方法之前;

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(Object, String)的具体流程如下。

@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException { Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
//遍历所有BeanPostProcessor的实现类,执行BeanPostProcessor的postProcessBeforeInitialization
//在InitDestroyAnnotationBeanPostProcessor中的实现是找出@PostConstruct标记的方法的定义信息,并执行
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}

@PreDestroy调用链如下:

@PreDestroy是通过org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor.postProcessBeforeDestruction(Object, String)被调用(InitDestroyAnnotationBeanPostProcessor实现了该接口),该方法的说明如下:

Apply this BeanPostProcessor to the given bean instance before its destruction. Can invoke custom destruction callbacks.

Like DisposableBean's destroy and a custom destroy method, this callback just applies to singleton beans in the factory (including inner beans).

调用时机: 该方法在组件的销毁之前调用;

org.springframework.beans.factory.support.DisposableBeanAdapter.destroy()的执行流程如下:

@Override
public void destroy() {
if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
//调用所有DestructionAwareBeanPostProcessor的postProcessBeforeDestruction方法
for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
processor.postProcessBeforeDestruction(this.bean, this.beanName);
}
} if (this.invokeDisposableBean) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking destroy() on bean with name '" + this.beanName + "'");
}
try {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
((DisposableBean) bean).destroy();
return null;
}
}, acc);
}
else {
//调用DisposableBean的销毁方法
((DisposableBean) bean).destroy();
}
}
catch (Throwable ex) {
String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
if (logger.isDebugEnabled()) {
logger.warn(msg, ex);
}
else {
logger.warn(msg + ": " + ex);
}
}
} //调用自定义的销毁方法
if (this.destroyMethod != null) {
invokeCustomDestroyMethod(this.destroyMethod);
}
else if (this.destroyMethodName != null) {
Method methodToCall = determineDestroyMethod();
if (methodToCall != null) {
invokeCustomDestroyMethod(methodToCall);
}
}
}

所以是先调用DestructionAwareBeanPostProcessor的postProcessBeforeDestruction(@PreDestroy标记的方法被调用),再是DisposableBean的destory方法,最后是自定义销毁方法。

好了,咱们今天就聊到这儿吧!别忘了给个在看和转发,让更多的人看到,一起学习一起进步!!

项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation

写在最后

如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习Spring注解驱动开发。公众号回复“spring注解”关键字,领取Spring注解驱动开发核心知识图,让Spring注解驱动开发不再迷茫。

【Spring注解驱动开发】@PostConstruct与@PreDestroy源码的执行过程的更多相关文章

  1. 0、Spring 注解驱动开发

    0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...

  2. 【Spring注解驱动开发】关于BeanPostProcessor后置处理器,你了解多少?

    写在前面 有些小伙伴问我,学习Spring是不是不用学习到这么细节的程度啊?感觉这些细节的部分在实际工作中使用不到啊,我到底需不需要学习到这么细节的程度呢?我的答案是:有必要学习到这么细节的程度,而且 ...

  3. 【Spring注解驱动开发】BeanPostProcessor在Spring底层是如何使用的?看完这篇我懂了!!

    写在前面 在<[String注解驱动开发]面试官再问你BeanPostProcessor的执行流程,就把这篇文章甩给他!>一文中,我们详细的介绍了BeanPostProcessor的执行流 ...

  4. 【spring 注解驱动开发】spring对象的生命周期

    尚学堂spring 注解驱动开发学习笔记之 - 生命周期 生命周期 1.生命周期-@Bean指定初始化和销毁方法 2.生命周期-InitializingBean和DisposableBean 3.生命 ...

  5. 【Spring注解驱动开发】聊聊Spring注解驱动开发那些事儿!

    写在前面 今天,面了一个工作5年的小伙伴,面试结果不理想啊!也不是我说,工作5年了,问多线程的知识:就只知道继承Thread类和实现Runnable接口!问Java集合,竟然说HashMap是线程安全 ...

  6. 【Spring注解驱动开发】使用@Scope注解设置组件的作用域

    写在前面 Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象.如果每次 ...

  7. 【Spring注解驱动开发】使用@Import注解给容器中快速导入一个组件

    写在前面 我们可以将一些bean组件交由Spring管理,并且Spring支持单实例bean和多实例bean.我们自己写的类,可以通过包扫描+标注注解(@Controller.@Servcie.@Re ...

  8. 【Spring注解驱动开发】在@Import注解中使用ImportSelector接口导入bean

    写在前面 在上一篇关于Spring的@Import注解的文章<[Spring注解驱动开发]使用@Import注解给容器中快速导入一个组件>中,我们简单介绍了如何使用@Import注解给容器 ...

  9. 【Spring注解驱动开发】面试官:如何将Service注入到Servlet中?朋友又栽了!!

    写在前面 最近,一位读者出去面试前准备了很久,信心满满的去面试.没想到面试官的一个问题把他难住了.面试官的问题是这样的:如何使用Spring将Service注入到Servlet中呢?这位读者平时也是很 ...

  10. 【Spring注解驱动开发】在@Import注解中使用ImportBeanDefinitionRegistrar向容器中注册bean

    写在前面 在前面的文章中,我们学习了如何使用@Import注解向Spring容器中导入bean,可以使用@Import注解快速向容器中导入bean,小伙伴们可以参见<[Spring注解驱动开发] ...

随机推荐

  1. es6 快速入门 系列 —— 解构

    其他章节请看: es6 快速入门 系列 解构 我们经常使用数组或对象存储数据,然后从中提取出相关数据信息 试图解决的问题 以前开发者为了从对象或数组中提取出特定数据并赋值给变量,编写了很多重复的代码, ...

  2. CORS就是跨域吗?

    首先,跨域的域是什么? 跨域的英文是:Cross-Origin. Origin 中文含义为:起源,源头,出生地. 在跨域中,"域"指的是一个 Web 资源(比如网页.脚本.图片等) ...

  3. DS12C887时钟模块, STC89和STC12的代码实现

    DS12C887是时钟芯片DS12C885集成了电池和晶振的版本. 如果拆掉DS12C887的外壳, 能看到里面就是DS12C885. 功能特性 能输出世纪.年.月.日.时.分.秒等时间信息 集成电池 ...

  4. mktemp命令

    mktemp命令 mktemp命令用于安全地创建一个临时文件或目录,并输出其名称,TEMPLATE在最后一个组件中必须至少包含3个连续的X,如果未指定TEMPLATE,则使用tmp.XXXXXXXXX ...

  5. win32-localtime的使用

    下面的例子用于反映本地系统的日期格式变化 // locale test #include <stdio.h> #include <locale.h> #include < ...

  6. 麒麟系统开发笔记(六):安装QtCreator开发IDE中的中文输入环境Fcitx输入法

    前言   中文输入法,QtCreator中无法输入中文也是ubuntu中一个常规问题,在麒麟系统中也此问题,要解决此问题,主要是安装和使用Fcitx输入法.  本文章最终结果是失败的,但是读者的系统未 ...

  7. 案例分享:Qt出版社书籍配套U盘资源播放器软件定制(脚本关联播放器与资源文件,播放器,兼容win7,win10和mac)

    红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术.树莓派.三维.OpenCV.OpenGL.ffmpeg.OSG.单片机.软硬结合等等)持续更新中-(点击传送门) 合作案例专栏:案例分享(体 ...

  8. 【LeetCode动态规划#01】动规入门:求斐波那契数 + 爬楼梯 + 最小代价爬楼梯(熟悉解题方法论)

    斐波那契数 力扣题目链接(opens new window) 斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 .该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和.也就 ...

  9. ThreadPoolExecutor和Executors的区别

    Executors创建的线程有两类 newFixedThreadPool和newSingleThreadExecutor,队列数量弹性,创建的线程的队列最大值为INTEGER.max容易造成OOM n ...

  10. 【Azure Data Lake Storage】如何才能保留Blob中的文件目录结构(即使文件夹中文件数量为0的情况下)?

    问题描述 在使用Azure Storage Account Blob时候,发现当文件夹中的Blob(文件)被全部输出后,文件夹也会消失? 为什么它不能像Windows系统中的文件夹一样,即使是一个空文 ...