Spring生命周期 Constructor > @PostConstruct > InitializingBean > init-method

时间:2022-09-07 08:38:35

项目中用到了 afterPropertiesSet: 于是具体的查了一下到底afterPropertiesSet到底是什么时候执行的。为什么一定要实现 InitializingBean;

**/
@Component
public class CityRepositoryImpl implements CityRepository, InitializingBean { private static final Logger LOGGER = LoggerFactory.getLogger(CityRepositoryImpl.class);
@Override
public void afterPropertiesSet() {
synchronized (CityRepositoryImpl.class) {
if (TEMPLATE_METHOD_MAP.size() == ) {
loadTemplateMethod();
}
}
} }}

Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,常用的设定方式有以下三种:

(1) 通过实现 InitializingBean/DisposableBean 接口来定制初始化之后/销毁之前的操作方法;
(2) 通过 <bean> 元素的 init-method/destroy-method属性指定初始化之后 /销毁之前调用的操作方法;
(3) 在指定方法上加上@PostConstruct 或@PreDestroy注解来制定该方法是在初始化之后还是销毁之前调用。 
这是我们就有个疑问,这三种方式是完全等同的吗,孰先孰后?

下面我们将带着这个疑问,试图通过测试代码以及分析Spring源码找到答案。

首先,我们还是编写一个简单的测试代码:

public class InitSequenceBean implements InitializingBean {

public InitSequenceBean() {
System.out.println("InitSequenceBean: constructor");
} @PostConstruct
public void postConstruct() {
System.out.println("InitSequenceBean: postConstruct");
} public void initMethod() {
System.out.println("InitSequenceBean: init-method");
} @Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitSequenceBean: afterPropertiesSet");
}
}

并且在配置文件中添加如下Bean定义:

<bean class="InitSequenceBean" init-method="initMethod"></bean>

好了,我们启动Spring容器,观察输出结果,就可知道三者的先后顺序了:

InitSequenceBean: constructor

InitSequenceBean: postConstruct

InitSequenceBean: afterPropertiesSet

InitSequenceBean: init-method

通过上述输出结果,三者的先后顺序也就一目了然了:

Constructor > @PostConstruct > InitializingBean > init-method

先大致分析下为什么会出现这些的结果:构造器(Constructor)被率先调用毋庸置疑,InitializingBean先于init-method我们也可以理解(在也谈Spring容器的生命周期中已经讨论过),但是PostConstruct为何率先于InitializingBean执行呢?

我们再次带着这个疑问去查看Spring源代码来一探究竟。

通过Debug并查看调用栈,我们发现了这个类org.springframework.context.annotation.CommonAnnotationBeanPostProcessor,从命名上,我们就可以得到某些信息——这是一个BeanPostProcessor。想到了什么?在也谈Spring容器的生命周期中,我们提到过BeanPostProcessor的postProcessBeforeInitialization是在Bean生命周期中afterPropertiesSet和init-method之前执被调用的。

再次观察CommonAnnotationBeanPostProcessor这个类,它继承自InitDestroyAnnotationBeanPostProcessor。InitDestroyAnnotationBeanPostProcessor顾名思义,就是在Bean初始化和销毁的时候所作的一个前置/后置处理器。

通过查看InitDestroyAnnotationBeanPostProcessor类下的postProcessBeforeInitialization方法:

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Couldn't invoke init method", ex);
}
return bean;
}

查看findLifecycleMetadata方法,继而我们跟踪到buildLifecycleMetadata这个方法体中,看下buildLifecycleMetadata这个方法体的内容:

private LifecycleMetadata buildLifecycleMetadata(final Class clazz) {
final LifecycleMetadata newMetadata = new LifecycleMetadata();
final boolean debug = logger.isDebugEnabled();
ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
if (initAnnotationType != null) {
if (method.getAnnotation(initAnnotationType) != null) {
newMetadata.addInitMethod(method);
if (debug) {
logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);
}
}
}
if (destroyAnnotationType != null) {
if (method.getAnnotation(destroyAnnotationType) != null) {
newMetadata.addDestroyMethod(method);
if (debug) {
logger.debug("Found destroy method on class [" + clazz.getName() + "]: " + method);
}
}
}
}
});
return newMetadata;
}

分析这段代码发现,在这里会去判断某方法有没有被initAnnotationType/destroyAnnotationType注释,如果有,则添加到init/destroy队列中,后续一一执行。

initAnnotationType/destroyAnnotationType注释是什么呢,我们在CommonAnnotationBeanPostProcessor的构造函数中看到下面这段代码:

/**
* Create a new CommonAnnotationBeanPostProcessor,
* with the init and destroy annotation types set to
* {@link javax.annotation.PostConstruct} and {@link javax.annotation.PreDestroy},
* respectively.
*/
public CommonAnnotationBeanPostProcessor() {
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
setInitAnnotationType(PostConstruct.class);
setDestroyAnnotationType(PreDestroy.class);
ignoreResourceType("javax.xml.ws.WebServiceContext");
}

一切都清晰了吧。一言以蔽之,@PostConstruct注解后的方法在BeanPostProcessor前置处理器中就被执行了,所以当然要先于InitializingBean和init-method执行了。

最后,给出本文的结论,Bean在实例化的过程中:

Constructor > @PostConstruct > InitializingBean > init-method

spring怎么使用注解在初始化bean的时候init-method指定的方法

用的三种指定特定操作的方法:
通过实现InitializingBean/DisposableBean 接口来定制初始化之后/销毁之前的操作方法;
通过<bean> 元素的 init-method/destroy-method属性指定初始化之后 /销毁之前调用的操作方法;
在指定方法上加上@PostConstruct或@PreDestroy注解来制定该方法是在初始化之后还是销毁之前调用。

参考:源码解析:init-method、@PostConstruct、afterPropertiesSet孰先孰后

参考:spring怎么使用注解在初始化bean的时候init-method指定的方法

Spring生命周期 Constructor > @PostConstruct > InitializingBean > init-method的更多相关文章

  1. Spring学习总结&lpar;4&rpar;-Spring生命周期的回调

    参考文档:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans ...

  2. 说下spring生命周期

    面试官:说下spring生命周期 程序员:不会 那你先回去等消息吧     Bean实现了BeanNameAware,Spring会将Bean的ID透传给setBeanName java.后端开发.程 ...

  3. spring生命周期

    Github地址 最近在整合mybatis-spring. 公司里面已经有一个叫做kylin-datasource的开发包,以前能够提供master和slave2个数据源,最近更新了2.0版本,支持自 ...

  4. Spring生命周期详解

    导读 Spring中Bean的生命周期从容器的启动到停止,涉及到的源码主要是在org.springframework.context.support.AbstractApplicationContex ...

  5. 【源码】spring生命周期

    一.spring生命周期 1. 实例化Bean 对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用crea ...

  6. 七、spring生命周期之初始化和销毁方法

    一.通过@Bean指定初始化和销毁方法 在以往的xml中,我们是这样配置的 <bean id="exampleInitBean" class="examples.E ...

  7. 八、spring生命周期之BeanPostProcessor

    BeanPostProcessor我们一般称为Bean的后置处理器,它与我们前面介绍的InitialingBean.init-method等一样,都是在bean的初始化时被调用,具体的用法我们在举例中 ...

  8. 玩转Spring生命周期之Lifecycle

    Lifecycle callbacks Initialization callbacks.Destruction callbacks要与容器的bean生命周期管理交互,即容器在启动后和容器在销毁前对每 ...

  9. spring生命周期流程图

    Spring作为当前Java最流行.最强大的轻量级框架,受到了程序员的热烈欢迎.准确的了解Spring Bean的生命周期是非常必要的.我们通常使用ApplicationContext作为Spring ...

随机推荐

  1. mysql配置以及性能优化(转)

    MySQL配置文件my.cnf中文详解,附mysql性能优化方法分享 ================================================================= ...

  2. clientX、pageX、scrollLeft、offsetLeft、clientWidth、screen&period;width的用法和区别

    一.定义和用法 1.event.clientX:事件对象的水平偏移量: event.clientY:事件对象的垂直偏移量: 2.event.pageX:事件对象加上滚动距离后的水平偏移量: event ...

  3. 异常之JSP页面跳转出错

    今天在开发过程中发现一个问题:在页面中使用了<jsp:forward>抛错Attempt to clear a buffer that's already been flushed!! 百 ...

  4. 判断字符串的首字母 ---------startsWith

    列: {                                            xtype : 'gridcolumn',                                ...

  5. AVL树插入操作实现

    为了提高二插排序树的性能,规定树中的每个节点的左子树和右子树高度差的绝对值不能大于1.为了满足上面的要求需要在插入完成后对树进行调整.下面介绍各个调整方式. 右单旋转 如下图所示,节点A的平衡因子(左 ...

  6. toggle笔记

    <!DOCTYPE html> <!-- saved from url=(0040)http://v3.bootcss.com/examples/carousel/ --> & ...

  7. pyqt columnView例子学习

    # -*- coding: utf-8 -*- # python:2.x __author__ = 'Administrator' from PyQt4.QtGui import  * from Py ...

  8. windbg命令学习1

    一.windbg 常用知识: 1. Windbg中的调试命令,分为三种:基本命令,元命令和扩展命令.基本命令和元命令是调试器自带的,元命令总是以“.”开头,而扩展命令是外部加入的,总是以感叹号“!”开 ...

  9. 分布式数据库Google Spanner原理分析

    Spanner 是Google的全球级的分布式数据库 (Globally-Distributed Database) .Spanner的扩展性达到了令人咋舌的全球级,可以扩展到数百万的机器,数已百计的 ...

  10. AlertConfirmDialog【基于AlertDialog的确认取消对话框】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 确认取消对话框,基于AlertDialog.不是基于DialogFragment. 按钮文本可以根据实际情况更换. 效果图 代码分析 ...