项目中用到了 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学习总结(4)-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. C-sizeof和strlen区别,以及sizeof如何计算结构体大小

    sizeof和strlen区别 sizeof是关键字,在编译时就能计算出值,可以计算任何类型 strlen是函数,只有在运行时才能去计算,且只能计算字符型的. 对于数组时,strlen是判断’\0’为 ...

  2. JS实现图片base64转blob对象,压缩图片,预览图片,图片旋转到正确角度

    base64转blob对象 /** 将base64转换为文件对象 * @param {String} base64 base64字符串 * */ var convertBase64ToBlob = f ...

  3. 算法题丨Remove Duplicates from Sorted Array

    描述 Given a sorted array, remove the duplicates in-place such that each element appear only once and ...

  4. 用WijmoJS搭建您的前端Web应用 —— React

    前文回顾 在本系列文章中,我们已经介绍了Angular和Vue框架下 WijmoJS 的玩法. 而今天,我们将展示如何使用 WijmoJS 来搭建一款具备独特创新性.出色性能和简单代码逻辑的 Reac ...

  5. android中的相对路径

    转载请标明出处:https://www.cnblogs.com/tangZH/p/9939655.html  1.同个文件夹访问 D:\Java\main\A.java D:\Java\main\B. ...

  6. Xamarin 打包生成 Android apk 文件

    Visual Studio 支持 apk 发布 Xamarin.Forms项目或Xamarin.Android项目开发完成之后需要发布.比较常规的发布方式是生成 apk 文件,微软也考虑到开发者有发布 ...

  7. ASP.NET Zero--前端应用程序

    前端应用程序 ASP.NET Zero包含可以作为您的公共网站或应用程序着陆页的起点的前端页面.首次运行项目时,您会看到主页如下所示: 这里有两页:主页和关于.这些页面的内容只是占位符和演示目的.您可 ...

  8. SQL Server 索引碎片产生原理重建索引和重新组织索引

    数据库存储本身是无序的,建立了聚集索引,会按照聚集索引物理顺序存入硬盘.既键值的逻辑顺序决定了表中相应行的物理顺序 多数情况下,数据库读取频率远高于写入频率,索引的存在 为了读取速度牺牲写入速度 页 ...

  9. 【转载】IIC SPI UART串行总线

    一.SPISPI(Serial Peripheral Interface,串行外设接口)是Motorola公司提出的一种同步串行数据传输标准,在很多器件中被广泛应用. 接口SPI接口经常被称为4线串行 ...

  10. redhat yum ISO 本地源

    先将ISO文件挂载起来: [root@racdb1 ~]# mount -o loop /opt/soft/rhel-server-6.8-x86_64-dvd.iso /mnt/iso [root@ ...