7.9.2 Bean销毁之前的行为

7.9.2 Bean销毁之前的行为

与定制初始化行为相似, Spring也提供两种方式定制Bean实例销毁之前的特定行为,这两种方式如下。

  1. 使用destroy-method属性。
  2. 实现DisposableBean接口。

第一种方式:使用destroy-method属性指定某个方法在Bean销毁之前被自动执行。使用这种方式不需要将代码与Spring的接口耦合在一起,代码污染小。
第二种方式:就是实现Disposable Bean接口,重写该接口内的方法destroy()方法,该方法定义为:void destroy() throws Exception

程序示例

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\lifecycle-destroy
└─src\
├─beans.xml
├─lee\
│ └─BeanTest.java
└─org\
└─crazyit\
└─app\
└─service\
├─Axe.java
├─impl\
│ ├─Chinese.java
│ └─SteelAxe.java
└─Person.java

Chinese.java

下面的示例程序让Chinese类既包括一个普通方法,但在配置时将该普通方法指定为生命周期方法;也让该Chinese类实现DisposableBean接口,因此也包含一个destroy()生命周期方法。该Chinese类的代码如下。

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
27
28
29
30
package org.crazyit.app.service.impl;

import org.springframework.beans.factory.DisposableBean;
import org.crazyit.app.service.*;

public class Chinese implements Person, DisposableBean
{
private Axe axe;
public Chinese()
{
System.out.println("Spring实例化主调bean:Chinese实例...");
}
public void setAxe(Axe axe)
{
System.out.println("Spring执行依赖关系注入...");
this.axe = axe;
}
public void useAxe()
{
System.out.println(axe.chop());
}
public void close()
{
System.out.println("正在执行销毁之前的方法 close...");
}
public void destroy() throws Exception
{
System.out.println("正在执行销毁之前的方法 destroy...");
}
}

上面程序中的粗体字代码定义了一个普通的close()方法,实际上这个方法的方法名是任意的,并不定是close()Spring也不会对这个close()方法进行任何特别的处理。只是配置文件使用destroy-method属性指定该方法是一个”生命周期方法”。
增加destroy-method="close"来指定close方法应在Bean实例销毁之前自动执行,如果它不实现DisposableBean接口,上面的Chinese类没有实现任何Spring的接口,只是增加一个普通的closed方法,它依然是一个普通的Java文件,没有代码污染。
上面程序中的destroy()方法是实现DisposableBean接口必须实现的方法。

beans.xml

在配置文件中增加destroy-method="close",指定close方法应该在Bean实例销毁之前自动被调用。
配置文件代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="steelAxe" class="org.crazyit.app.service.impl.SteelAxe"/>
<!-- 配置chinese Bean,使用destroy-method="close"
指定该Bean实例被销毁之前,Spring会自动执行指定该Bean的close方法 -->
<bean id="chinese" class="org.crazyit.app.service.impl.Chinese"
destroy-method="close">
<property name="axe" ref="steelAxe"/>
</bean>
</beans>

配置该Bean与配置普通Bean没有任何区别, Spring可以自动检测容器中的DisposableBean,在销毁Bean实例之前, Spring容器会自动调用该Bean实例的destroy()方法。
singleton作用域的Bean通常会随容器的关闭而销毁,但问题是:ApplicationContext容器在什么时候关闭呢?

Web应用关闭时会自动关闭Spring容器

在基于WebApplicationContext实现中,系统已经提供了相应的代码保证关闭Web应用时恰当地关闭Spring容器。

在非Web环境如何关闭Spring容器

如果处于一个非Web应用的环境下,为了让Spring容器优雅地关闭,并调用singleton Bean上的相应析构回调方法,则需要在JVM里注册一个关闭钩子( shutdown hook),这样就可保证Spring容器被恰当关闭,且自动执行singleton Bean实例的析构回调方法。

如何注册关闭钩子来关闭Spring容器

为了注册关闭钩子,只需要调用在AbstractApplicationContext中提供的registerShutdownHook()方法即可。

BeanTest.java

看如下主程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package lee;

import org.crazyit.app.service.Person;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeanTest
{
public static void main(String[] args)
{
// 以CLASSPATH路径下的配置文件创建ApplicationContext
@SuppressWarnings("resource")
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(
"beans.xml");
// 获取容器中的Bean实例
Person p = ctx.getBean("chinese", Person.class);
p.useAxe();
// 为Spring容器注册关闭钩子
ctx.registerShutdownHook();
}
}

上面程序的最后一行粗体字代码为Spring容器注册了一个关闭钩子,程序将会在退出JVM之前优雅地关闭Spring容器,并保证关闭Spring容器之前调用singleton Bean实例的析构回调方法。

运行结果

运行上面的程序,将可以看到程序退出执行输出如下两行:

1
2
3
...
正在执行销毁之前的方法 destroy...
正在执行销毁之前的方法 close...

通过上面的执行结果可以看出,在Spring容器关闭之前,注入之后,程序先调用destroy()方法进行回收资源,再调用close()方法进行回收资源。

不推荐实现DisposableBean接口方式

如果某个Bean类实现了DisposableBean接口,在Bean实例被销毁之前, Spring容器会自动调用该Bean实例的destroy()方法。其执行结果与采用destroy-method属性指定生命周期方法几乎一样。但实现DisposableBean接口污染了代码,是侵入式设计,因此不推荐采用。
对于实现Disposable Bean接口的Bean,无须使用destroy-method属性来指定销毁之前的方法,配置该Bean实例与配置普通Bean实例完全一样, Spring容器会自动检测Bean实例是否实现了特定生命周期接口,并由此决定是否需要执行生命周期方法。

先执行DisposableBean接口方法再执行destroy-method属性指定的方法

如果既采用destroy-method属性指定销毁之前的方法,又采用实现DisposableBean接来指定销毁之前的方法, Spring容器会执行两个方法:先执行DisposableBean接口中定义的方法,然后执行destroy-method属性指定的方法

在beans元素上为所有bean指定生命周期行为

除此之外,如果容器中的很多Bean都需要指定特定的生命周期行为,则可以利用<beans>元素的default-init-method属性和default-destroy-method属性,这两个属性的作用类似于<bean>元素的init-methoddestroy-method属性的作用,区别是default-init-method属性和default-destroy- method属性是属于<beans>元素的,它们将使容器中的所有Bean生效。

1
2
3
<beans default-init-method="init">
...
</beans>

上面配置片段中的代码指定了default-init-method="init",这意味着只要Spring容器中的某个Bean实例具有init()方法, Sprin就会在该Bean的所有依赖关系被设置之后,自动调用该Bean实例的init()方法。

Spring容器中Bean实例完整的生命周期行为

图7.12显示了Spring容器中Bean实例完整的生命周期行为:
这里有一张图片