0%

spring中bean的生命周期回调

简介

为了与容器对Bean生命周期的管理进行交互,你可以实现Spring InitializingBeanDisposableBean 接口。容器为前者调用 afterPropertiesSet(),为后者调用 destroy()

JSR-250的 @PostConstruct@PreDestroy 注解通常被认为是在现代Spring应用程序中接收生命周期回调的最佳实践。使用这些注解意味着你的Bean不会被耦合到Spring特定的接口。详情请参见 使用 使用 @PostConstruct@PreDestroy

如果你不想使用JSR-250注解,但你仍然想消除耦合,可以考虑用 init-methoddestroy-method bean 定义元数据。

初始化回调

org.springframework.beans.factory.InitializingBean 接口让Bean在容器对Bean设置了所有必要的属性后执行初始化工作。InitializingBean 接口指定了一个方法。

1
void afterPropertiesSet() throws Exception;
1
2
3
4
5
6
7
public class AnotherExampleBean implements InitializingBean {

@Override
public void afterPropertiesSet() {
// do some initialization work
}
}

建议使用 @PostConstruct 注解或指定一个POJO初始化方法。在基于XML的配置元数据中,你可以使用 init-method 属性来指定具有 void 无参数签名的方法的名称。对于Java配置,你可以使用 @BeaninitMethod 属性

销毁回调

实现 org.springframework.beans.factory.DisposableBean 接口可以让Bean在包含它的容器被销毁时获得一个回调。DisposableBean 接口指定了一个方法。

1
void destroy() throws Exception;

建议使用 @PreDestroy 注解或指定一个bean定义所支持的通用方法。对于基于XML的配置元数据,你可以使用 <bean/> 上的 destroy-method 属性。使用Java配置,你可以使用 @BeandestroyMethod 属性。

1
2
3
4
5
6
7
public class AnotherExampleBean implements DisposableBean {

@Override
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}

默认的初始化和销毁方法

在spring可以配置全局默认的初始化和销毁方法,不用为每个bean来每次指定初始化和销毁方法。

1
2
3
4
5
6
7
<beans default-init-method="init" default-destroy-method="destory">

<bean id="blogService" class="com.something.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>

</beans>

结合生命周期机制

从Spring 2.5开始,有三个选项来控制Bean的生命周期行为。

如果一个bean设置了多种生命周期回调方法,并且方法名都不相同,则初始化时调用顺序如下:

  1. 注解了 @PostConstruct 的方法。
  2. afterPropertiesSet(),如 InitializingBean 回调接口所定义。
  3. 一个自定义配置的 init() 方法。

销毁方法的调用顺序是一样的。

  1. 注解了 @PreDestroy 的方法。
  2. destroy(),正如 DisposableBean 回调接口所定义的那样。
  3. 一个自定义配置的 destroy() 方法。

结合源码

在spring中bean的创建中,在被实例化后,首先会使用 Bean 定义中的属性值填充给定 BeanWrapper 中的 Bean 实例。接下来调用BeanPostProcessor中的postProcessBeforeInitialization的方法,@PostConstruct@PreDestroy注解会被CommonAnnotationBeanPostProcessor执行,所以这两个注解会最先执行,之后会调用invokeInitMethods方法来调用初始化方法,例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {

//首先判断是否实现了InitializingBean接口,即与spring耦合的初始化方法
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
((InitializingBean) bean).afterPropertiesSet();
}

//之后获取自定义的init方法 例如通过xml文件<bean <init-method=>/>或着通过@Bean(init-method)声明的
if (mbd != null && bean.getClass() != NullBean.class) {
String[] initMethodNames = mbd.getInitMethodNames();
if (initMethodNames != null) {
for (String initMethodName : initMethodNames) {
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd, initMethodName);
}
}
}
}
}