Spring 中Bean的生命周期与InitializingBean和DisposableBean
在内部,Spring使用BeanPostProcessor来处理它能找到的标志接口以及调用适当的方法,如果你需要自定义的特性或者其他的spring没有提供的生命周期的行为,你可以实现自己的BeanPostProcessor
initializingBean/init-method
Spirng的InitializingBean为bean提供了定义初始化方法的方式
InitializingBean是一个接口,它仅仅包含一个方法:afterPropertiesSet()
实现org.springframework.beans.factory.InitializingBean接口允许一个bean在它的所有必须属性被BeanFactory设置后,来执行初始化的工作,InitialzingBean仅仅指定了一个方法。
注意通常InitializingBean接口的使用是能够被避免的,(不鼓励使用,因为没有必要把代码通Spring耦合起来) Bean的定义支持指定一个普通的初始化方法,在使用XmlBeanFactory的情况下,可以通过指定init-method属性来完成
<baan id="exampleInitBean" class="example.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init(){
//do some initialization work;
}
}
同下面的是一样的
<bean id="exampleInitBean" class="example.AnotherExampleBean" />
public class AnotherExampleBean implements InitializingBean{
public void afterPropertiesSet(){
//do some initialization work
}
}
但却不把代码耦合于spring中
DisposableBean/destroy-method
实现org.springframework.beans.factory.DisposableBean接口的bean允许在容器销毁该bean的时候获得一次回调。DisposableBean接口也只规定了一个方法:
void destroy() throws Exception;
通常,要避免使用DisposableBean标志接口而且不鼓励使用该接口,因为这样会将代码与Spring耦合在一起,有一个可选的方案是,在bean定义中指定一个普通的析构方法,然后在XML配置文件中通过指定destroy-method属性来完成。如下面的定义所示:
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
...效果与下面完全一样...
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
... 但是没有将代码与Spring耦合在一起。
缺省的初始化和析构方法<beans default-init-method="init">
如果有人没有采用Spring所指定的InitializingBean和DisposableBean回调接口来编写初始化和析构方法回调,会发现自己正在编写的方法,其名称莫过于init(), initialize(),dispose()等等。这种生命周期回调方法的名称最好在一个项目范围内标准化,这样团队中的开发人员就可以使用同样的方法名称,并且确保了某种程度的一致性。
Spring容器通过配置可以实现对每个 bean初始化时的查找和销毁时的回调调用。这也就是说,一个应用的开发者可以借助于初始化的回调方法init() 轻松的写一个类(不必想XML配置文件那样为每个bean都配置一个'init-method="init"'属性)。Spring IoC容器在创建bean的时候将调用这个方法 (这和之前描述的标准生命周期回调一致)。
为了完全弄清如何使用该特性,让我们看一个例子。出于示范的目的,假设一个项目的编码规范中约定所有的初始化回调方法都被命名为init()而析构回调方法被命名为destroy()。遵循此规则写成的类如下所示:
public class DefaultBlogService implements BlogService {
private BlogDao blogDao;
public void setBlogDao(BlogDao blogDao) {
this.blogDao = blogDao;
}
// this is (unsurprisingly) the initialization callback method
public void init() {
if (this.blogDao == null) {
throw new IllegalStateException("The [blogDao] property must be set.");
}
}
}
<beans default-init-method="init">
<bean id="blogService" class="com.foo.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
注意在顶级的<beans/>元素中的'default-init-method'属性。这个属性的含义是Spring IoC容器在bean创建和装配的时候会将'init'方法作为实例化回调方法。如果类有这个方法,则会在适当的时候执行。
销毁回调方法配置是相同的 (XML配置),在顶级的<beans/>元素中使用 'default-destroy-method' 属性。
使用这个功能可以把你从位每个bean指定初始化和销毁回调的繁杂工作中解救出来。为了一致性,应该强制性的为初始化和销毁回调方法采用一致的命名规则。
当已经存在的类的初始化方法的命名规则与惯例有差异的时候,你应该始终使用<bean/>元素中的'init-method'和'destroy-method'属性(在XML配置中)来覆盖默认的方式。
最后,请注意Spring容器保证在bean的所有依赖都满足后立即执行配置的初始化回调。这意味着初始化回调在原生bean上调用,这也意味着这个时候任何诸如AOP拦截器之类的将不能被应用。一个目标bean是首先完全创建,然后才应用诸如AOP代理等拦截器链。注意,如果目标bean和代理是分开定义了,你的代码甚至可以绕开代理直接和原生bean通信。因此,在初始化方法上使用拦截器将产生未知的结果,因为这将目标bean和它的代理/拦截器的生命周期绑定并且留下了和初始bean直接通信这样奇怪的方式。