Spring可以管理singleton作用域Bean的生命周期,可以精确地知道该Bean何时被创建、何时被初始化完成、容器何时准备注销该Bean实例。
管理Bean的生命周期行为主要有如下两个时机:
> 注入依赖关系之后
> 即将销毁Bean之前
对于prototype作用域的Bean,Spring容器仅仅负责创建,当容器创建了Bean实例之后,Bean实例完全交给客户端代码管理,容器不再跟踪其生命周期。
1、依赖关系注入之后的行为
Spring提供两种方式在Bean全部属性设置成功后执行特定行为:
> 使用init-method属性
> 实现InitializingBean接口
第一种方式:使用init-method属性指定某个方法应在Bean全部依赖关系设置结束后自动执行。该方法无需将代码与Spring接口耦合在一起,代码污染小。
第二种方式也可以达到同样的效果,就是让Bean类实现InitializingBean接口,该接口提供一个方法:void afterPropertiesSet() throws Exception。
package LifecycleOfBean;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import DependencyInjection.Axe;
import DependencyInjection.Person;
public class Chinese implements Person ,InitializingBean
{
private Axe axe;
public Chinese(){
System.out.println("Spring实例化主调bean:Chinese实例...");
}
public void setAxe(Axe axe) {
System.out.println("Spring执行依赖注入...");
this.axe = axe;
}
@Override
public void useAxe() {
// TODO Auto-generated method stub
System.out.println(axe.chop());
}
//测试用初始化方法
public void init(){
System.out.println("正在执行初始化方法init.");
}
//实现InitializingBean接口必须实现的方法
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("正在执行初始化方法afterPropertiesSet..");
}
}
上面的程序定义了一个普通的init()方法,实际上这个方法的方法名是任意的,并不一定是init()方法,Spring也不会对这个init()方法进行任何特别的处理。只是接下来会在配置文件中用 init-method 属性指定该方法时一个“生命周期”。
增加 init-method="init" 来指定init() 方法应在Bean的全部属性设置结束后自动执行,如果它不实现InitializingBean接口,上面的Chinese类没有实现任何Spring接口,只是增加一个普通的init() 方法,它依然是一个普通的Java文件,没有代码污染。
<?xml version="1.0" encoding="UTF-8"?>
<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-4.2.xsd"
>
<bean id="steelAxe" class="DependencyInjection.SteelAxe"/>
<bean id="chineseLC" class="LifecycleOfBean.Chinese"
init-method="init">
<property name="axe" ref="steelAxe"/>
</bean>
</beans>
package LifecycleOfBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import DependencyInjection.Person;
public class Test {
public static void main(String[] args) {
ApplicationContext ctx =
new ClassPathXmlApplicationContext("bean.xml");
Person p = ctx.getBean("chineseLC",Person.class);
p.useAxe();
}
}
对于实现InitializingBean接口的Bean,无须使用init-method 属性来指定初始化方法,配置该Bean实例与配置普通Bean实例完全一样,Spring容器会自动检测Bean实例是否实现了特定生命周期接口,并由此决定是否需要执行生命周期方法。
如果某个Bean类实现InitializingBean 接口,当该Bean的所有依赖关系被设置完成后,Spring容器会自动调用该Bean实例的afterPropertiesSet() 方法。其执行结果与采用 init-method 属性指定生命周期方法几乎一样。但实现InitializingBean 接口污染了代码,是侵入式设计,因此不推荐使用。
如果既采用 init-method 属性指定初始化方法,又实现InitializingBean 接口来指定初始化方法,容器会执行两个初始化方法:先执行InitializingBean 接口中定义的方法,然后执行init-method属性指定的方法。
2、Bean销毁之前的行为
与定制初始化行为相似,Spring也提供两种方法定制Bean实例销毁之前的特定行为,这两种方式如下:
> 使用destroy-method属性
> 实现DisposableBean接口
第一种方式:使用destroy-method属性指定某个方法应在Bean销毁之前被自动执行。使用这种方法,不需要将代码与Spring接口耦合在一起,代码污染小。
第二种方式也可以达到同样的效果,就是让Bean类实现DisposableBean接口,该接口提供一个方法:void destroy() throws Exception。
package LifecycleOfBean;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import DependencyInjection.Axe;
import DependencyInjection.Person;
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;
}
@Override
public void useAxe() {
// TODO Auto-generated method stub
System.out.println(axe.chop());
}
public void close(){
System.out.println("正在执行销毁前方法close.");
}
@Override
public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("正在执行销毁前方法destroy..");
}
}
上面的程序定义了一个普通的close()方法,实际上这个方法的方法名是任意的,并不一定是close()方法,Spring也不会对这个close()方法进行任何特别的处理。只是接下来会在配置文件中用 destroy-method 属性指定该方法时一个“生命周期”。
增加 destroy-method="close" 来指定close() 方法应在Bean实例销毁之前自动执行,如果它不实现DisposableBean接口,上面的Chinese类没有实现任何Spring接口,只是增加一个普通的close() 方法,它依然是一个普通的Java文件,没有代码污染。
<?xml version="1.0" encoding="UTF-8"?>
<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-4.2.xsd"
>
<bean id="chineseLC" class="LifecycleOfBean.Chinese"
destroy-method="close">
<property name="axe" ref="steelAxe"/>
</bean>
</beans>
singleton作用域的Bean通常会随容器的关闭而销毁,但问题是:ApplicationContext容器在什么时候关闭呢?在基于Web的ApplicationContext实现中,系统已经提供了相应的代码保证关闭Web应用时恰当地关闭Spring容器。
如果处于一个非Web应用的环境下,为了让Spring容器优雅地关闭,并调用singleton Bean上的相应析构回调方法,则需要再JVM里注册一个关闭钩子(shutdown hook),这样就可保证Spring容器被恰当关闭,且自动执行singleton Bean实例的析构回调方法。
为了注册关闭钩子,只需要调用AbstractApplicationContext中提供的registerShutdownHook() 方法既可。
package LifecycleOfBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import DependencyInjection.Person;
public class Test {
public static void main(String[] args) {
ApplicationContext ctx =
new ClassPathXmlApplicationContext("bean.xml");
Person p = ctx.getBean("chineseLC",Person.class);
p.useAxe();
//为Spring容器注册关闭钩子
((AbstractApplicationContext) ctx).registerShutdownHook();
}
}
如果某个Bean类实现DisposableBean 接口,在Bean被销毁之前,Spring容器会自动调用该Bean实例的destroy() 方法。其执行结果与采用destroy-method 属性指定生命周期方法几乎一样。但实现DisposableBean 接口污染了代码,是侵入式设计,因此不推荐使用。
对于实现DisposableBean接口的Bean,无须使用destroy-method 属性来指定销毁之前的方法,配置该Bean实例与配置普通Bean实例完全一样,Spring容器会自动检测Bean实例是否实现了特定生命周期接口,并由此决定是否需要执行生命周期方法。
如果既采用 destroy-method 属性指定销毁之前的方法,又采用实现DisposableBean 接口来指定销毁之前的方法,容器会执行两个初始化方法:先执行DisposableBean 接口中定义的方法,然后执行destroy-method属性指定的方法。
除此之外,如果容器中有很多Bean都需要指定特定的生命周期行为,则可以利用<beans.../>元素的 default-init-method 属性和 default-destroy-method 属性,这两个属性的作用类似于<bean.../>元素的 init-method 属性和 destroy-method 属性,关键是 default-init-method 属性和 default-destroy-method 属性是属于<beans.../>元素的,它们将使容器中所有Bean生效。
Spring容器中Bean实例完整的生命周期行为:
开始 → 创建实例 → 注入依赖关系 → 调用afterPropertiesSet → 调用init-method 方法 → 对外提供服务 → 调用destroy → 调用destroy-method 方法→ Bean实例被销毁
需要指出的是,当Bean实现了ApplicationContextAware接口、BeanNameAware接口之后,Spring容器会在该Bean初始化完成之后——也就是调用init-method 属性所指定的方法(如果有)之后,再来回调setApplicationContext(ApplicationContext applicationContext)、setBeanName(String name) 方法。