9 生命周期回调方法
对于Spring bean
来讲,我们默认可以指定两个生命周期回调方法。一个是在ApplicationContext
将bean
初始化,包括注入对应的依赖后的回调方法;另一个是在ApplicationContext
准备销毁之前的回调方法。要实现这种回调主要有三种方式:实现特定的接口、在XML
配置文件中指定回调方法和使用JSR-250
标准的注解。
9.1 实现特定接口
针对bean
初始化后的回调和ApplicationContext
销毁前的回调,Spring
分别为我们了提供了InitializingBean
和DisposableBean
接口供用户实现,这样Spring
在需要进行回调时就会调用对应接口提供的回调方法。
9.1.1 InitializingBean
InitializingBean
是用来定义ApplicationContext
在完全初始化一个bean
以后需要需要回调的方法的,其中只定义了一个afterPropertiesSet()
方法。如其名称所描述的那样,该方法将在ApplicationContext
将一个bean
完全初始化,包括将对应的依赖项都注入以后才会被调用。InitializingBean
的完全定义如下。
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
由于InitializingBean
的afterPropertiesSet()
方法会在依赖项都进行注入以后再回调,所以该方法通常会用来检查必要的依赖注入,以使我们能够在bean
被初始化时就发现其中的错误,而不是在很长时间使用以后才发现。如果你去看Spring
的源码,你就会发现源码中有很多InitializingBean
的使用,而且基本都是用来检查必要的依赖项是否为空的。
public class Hello implements InitializingBean {
private World world;
/**
* 该方法将在当前bean被完全初始化后被调用
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(world, "world should not be null.");
}
public void setWorld(World world) {
this.world = world;
}
}
9.1.2 DisposableBean
DisposableBean
是用来定义在ApplicationContext
销毁之前需要回调的方法的。DisposableBean
接口中只定义了一个destroy()
方法,在ApplicationContext
被销毁前,Spring
将依次调用bean
容器中实现了DisposableBean
接口的destroy()
方法。所以,我们可以通过实现该接口的destroy()
方法来达到在ApplicationContext
销毁前释放某些特定资源的目的。
public interface DisposableBean {
void destroy() throws Exception;
}
在Spring
的源码中,也有很多实现了DisposableBean
接口的类,如我们熟悉的ApplicationContext
实现类、SingleConnectionDataSource
等。
9.2 在XML中配置回调方法
在XML
配置文件中通过bean
元素定义一个bean
时,我们可以通过bean元素的init-method
属性和destroy-method
属性来指定当前bean在初始化以后和ApplicationContext
销毁前的回调方法。需要注意的是所指定的回调方法必须是没有参数的。
通过init-method
属性来指定初始化方法时所对应的方法必须是该bean中所拥有的方法,所以首先我们需要在对应的bean中定义对应的初始化方法,这里假设我们需要在bean中定义一个init()
方法作为该bean的初始化方法,那么我们可以对我们的bean进行类似如下定义。
public class Hello {
private World world;
/**
* 该方法将被用来作为初始化方法,在当前bean被完全初始化后被调用
*/
public void init() {
Assert.notNull(world, "world should not be null.");
}
public void setWorld(World world) {
this.world = world;
}
}
接下来就是在XML配置文件中定义该bean时通过init-method
属性定义对应的初始化方法为init()
方法,init-method
属性的属性值就对应初始化方法的名称,所以我们的bean应该是如下定义。
<bean name="world" class="com.app.World"/>
<!-- 通过init-method属性指定初始化方法名称 -->
<bean id="hello" class="com.app.Hello" init-method="init">
<property name="world" ref="world"/>
</bean>
init-method
和destroy-method
的用法和配置等基本上都是一样的,所以对于使用destroy-method
来指定ApplicationContext
销毁前的回调方法的用法就不再赘述了。
如果我们的初始化方法或销毁方法的名称大都是一样的,在通过init-method
和destroy-method
进行指定的时候我们就没有必要一个个bean
都去指定了,Spring允许我们在最顶级的beans元素上指定默认的初始化后回调方法和销毁前的回调方法名称,这样对于没有指定init-method
或destroy-method
的bean将默认将其中default-init-method
或default-destroy-method
属性值对应名称的方法(如果存在的话)视为初始化后的回调方法或销毁前的回调方法。这是通过default-init-method
和default-destroy-method
属性来定义的。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-init-method="init" default-destroy-method="destroy">
</beans>
以上表示定义默认的初始化后回调方法名称为init,默认的销毁前回调方法名称为destroy。
当定义了default-init-method
或default-destroy-method
以后,如果我们的某个bean对应的初始化后回调方法名称或销毁前的回调方法名称与默认定义的不一样,则我们可以在对应的bean上通过init-method
或destroy-method
指定该bean自身的回调方法名称,即bean上定义的回调方法名称将会比默认定义拥有更高的优先级。
9.3 使用JSR-250标准的注解
关于bean的生命周期回调方法,Spring也会JSR-250
标准注解做了支持,即在bean完全初始化后将回调使用@PostConstruct
标注的方法,在销毁ApplicationContext
前将回调使用@PreDestroy
标注的方法。
针对之前的示例,如果我们现在把定义的bean定义成如下这样,即没有在bean上通过init-method
和destroy-method
指定初始化方法和销毁方法。
<bean name="world" class="com.app.World"/>
<bean id="hello" class="com.app.Hello">
<property name="world" ref="world"/>
</bean>
当然,这里也不考虑全局性的init-method
和destroy-method
方法,如果我们希望在id为“hello”的bean被初始化后回调其中的init()
方法,在销毁前回调其中的destroy()
方法,我们就可以通过@PostConstruct
和@PreDestroy
进行如下定义。
public class Hello {
private World world;
/**
* 该方法将被用来作为初始化方法,在当前bean被完全初始化后被调用
*/
@PostConstruct
public void init() {
Assert.notNull(world, "world should not be null.");
}
@PreDestroy
public void destroy() {
System.out.println("---------destroy-----------");
}
public void setWorld(World world) {
this.world = world;
}
}
使用JSR-250
标准指定初始化后的回调方法以及销毁前的回调方法时,如果我们希望将多个方法都作为对应的回调方法进行回调,则可以在多个方法上同时使用对应的注解进行标注,Spring
将依次执行对应的方法。
public class Hello {
private World world;
@PostConstruct
public void init() {
System.out.println("-----------init-------------");
}
/**
* 该方法将被用来作为初始化方法,在当前bean被完全初始化后被调用
*/
@PostConstruct
public void init2() {
Assert.notNull(world, "world should not be null.");
}
@PreDestroy
public void destroy() {
System.out.println("------------destroy----------------");
}
@PreDestroy
public void destroy2() {
System.out.println("---------destroy2-----------");
}
public void setWorld(World world) {
this.world = world;
}
}
9.4 混合使用三种方式
Spring
允许我们混合使用上述介绍的三种方式来指定对应的回调方法。当对于同一个bean使用三种方式指定了同一个方法作为初始化后的回调方法或销毁前的回调方法,则对应的回调方法只会被执行一次。然而,当对于同一个bean使用两种或三种方式指定的回调方法不是同一个方法时,Spring
将依次执行使用不同的方式指定的回调方法。对于初始化后的回调方法而言,具体规则如下:
- 使用@PostConstruct标注的方法。
- 实现InitializingBean接口后的回调方法afterPropertiesSet()方法。
- 通过init-method或default-init-method指定的方法。
对于销毁前的回调方法而言,其规则是一样的:
- 使用@PreDestroy标注的方法。
- 实现DisposableBean接口后的回调方法destroy()方法。
- 通过destroy-method或default-destroy-method指定的方法。
(注:本文是基于Spring4.1.0所写)