参考:官方文档
1 Spring框架中的生命周期回调
Spring
框架提供了一些接口,能够让bean
感知生命周期。
1.1 Bean的生命周期回调
Spring
容器提供了两个接口可以让容器管理的bean
感知生命周期:
-
InitializingBean
:提供了一个回调函数afterPropertiesSet()
,在bean
初始化的时候被调用。 -
DisposableBean
:提供了一个回调函数destroy()
,在bean
被销毁之前被调用。
JSR-250
提供了@PostConstruct
和@PreDestroy
注解实现相同的功能。如果在
xml
中配置bean
,可以使用init-method
和destroy-method
实现相同的功能。
在Spring
内部,Spring
框架使用的是BeanPostProcessor
的实现类来处理任何可以找到的生命周期回调函数。如果想定制Spring
提供的生命周期回调以外的回调函数,可以自己实现BeanPostProcessor
接口。更多的信息可以参考Container Extension Points。
除了前边的两个回调,Spring
还提供了Lifecycle
接口,可以让bean
感知容器的生命周期。
注意:
InitializingBean
、DisposableBean
与Lifecycle
的不同点在于:
InitializingBean
、DisposableBean
提供的回调是建立在Spring
容器已经启动完毕的前提之下,它们提供的回调函数只能让bean
感知自己被初始化和销毁的生命周期。Lifecycle
接口提供了让bean
去感知Spring
容器本身的启动和停止生命周期的能力。
1.1.1 初始化时的回调
Spring
提供的org.springframework.beans.factory.InitializingBean
接口,允许bean
在Spring
容器为它设置完毕所有有效的properties
之后执行初始化工作。InitializingBean
接口只有一个函数:
void afterPropertiesSet() throws Exception;
Spring
官方建议不要去实现这个接口,因为它将代码耦合到你的bean
中了。官方建议使用@PostConstruct 替换它,或者指定初始化方法:
- 在
xml
配置中,使用init-method
指定初始化方法。 - 在基于
Java
代码的配置中,可以在@Bean
注解中使用属性initMethod
指定初始化方法,参见 Receiving Lifecycle Callbacks。
public class ExampleBean {
public void init() {
// do some initialization work
}
}
下面是xml
中的配置:
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
下面是Java
中的配置:
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public ExampleBean exampleBean() {
return new ExampleBean();
}
}
1.1.2 销毁时的回调
Spring
提供了org.springframework.beans.factory.DisposableBean
接口,用于在bean
被容器销毁之前执行一些动作。DisposableBean
接口只有一个方法:
void destroy() throws Exception;
同样的,Spring
官方也不建议直接实现这个接口,而是改用@PreDestroy 注解,或者指定销毁方法:
- 在
xml
配置中,使用destroy-method
指定销毁方法。 - 在
Java
配置中,使用@Bean
注解的destroyMethod
属性指定销毁方法,参见 Receiving Lifecycle Callbacks 。
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
下面是xml
中的配置:
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
下面是Java
中的配置:
@Configuration
public class AppConfig {
@Bean(destroyMethod = "cleanup")
public ExampleBean exampleBean() {
return new ExampleBean();
}
}
1.2 容器启动和停止回调
Spring
框架还提供了接口,能够让bean
感知容器的生命周期。
1.2.1 Lifecycle接口
Lifesysle
接口提供了感知Spring
容器生命周期的方法:
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
任何被Spring
容器管理的bean
都可以实现Lifecycle
接口。当ApplicationContext
本身接收到启动和停止的信号时,它会调用这个上下文中所有Lifecycle
接口的实现类。
1.2.2 LifecycleProcessor接口
它将处理委托给LifecycleProcessor
的实现:
public interface LifecycleProcessor extends Lifecycle {
void onRefresh();
void onClose();
}
LifecycleProcessor
接口除了继承Lifecycle
接口中的3个方法之外,本身还定义了两个方法:
-
onRefresh()
方法,处理Spring
容器refreshed
事件。 -
onClose()
方法,处理Spring
容器closed
事件。
注意:
org.springframework.context.Lifecycle
接口只对显示的启动和停止事件有反应,而对上下文刷新后的自动启动没有反应。如果想对自动启动有反应,可以使用org.springframework.context.SmartLifecycle
接口。此外,停止通知并不一定在销毁通知之前到来。在通常的容器关闭过程中,所有实现
Lifecycle
接口的bean
首先会收到容器停止的通知,然后才是bean
的销毁通知。然而,在热更新或终止刷新的时候,只有销毁通知。
1.2.3 SmartLifecycle接口
有时候,如果两个bean
有依赖关系,这种依赖关系不一定是引用的关系,也有可能是希望某些bean
先进行初始化,等初始化完成之后再初始化另一些bean
。由于bean
的初始化和销毁的时机不确定性,有时候会导致依赖项为null
。
在这种场景下,可以使用SmartLifecycle
接口。SmartLifecycle
接口继承了Phased
接口,定义了一个getPhase()
方法:
public interface Phased {
int getPhase();
}
下边是SmartLifecycle
接口的定义:
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
getPhase()
方法返回一个int
型数字,表明执行顺序。
启动时,getPhase()
值最小的先启动,停止时最后停止。
因此,若对象实现了SmartLifecycle
接口,它的getPhase()
方法返回Integer.MIN_VALUE
,那么该对象最先启动,最后停止。若对象的getPhase()
方法返回Integer.MAX_VALUE
,那么该对象最后启动最先停止。关于phase
的值,那些没有实现SmartLifecycle
接口的Lifecycle
对象,其值默认为0
。因此,负phase
值表示要这些Lifecycle
对象之前启动(在这些Lifecycyle
对象之后停止),使用正值则恰恰相反。
SmartLifecycle
中的stop()
方法有一个回调参数。所有的实现在关闭处理完成后会调用回调的run()
方法,相当于开启异步关闭功能。
LifecycleProcessor
接口在Spring
中的默认实现是DefaultLifecycleProcessor
类,该类会为每个回调等待超时,默认超时是30
秒。该类在容器内默认bean
名称是lifecycleProcessor
,你可以修改默认的参数,比如修改默认超时时间:
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
<!-- timeout value in milliseconds -->
<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>
如前所述,LifecycleProcessor
接口还定义了用于刷新和关闭上下文的回调方法。 后者驱动关机进程,就像显式调用stop()一样,但它发生在上下文关闭时。另一方面,“刷新”回调支持SmartLifecycle bean的另一个特性。当上下文被刷新(在所有对象被实例化和初始化之后)时,将调用该回调。此时,默认的生命周期处理器将检查每个SmartLifecycle对象的isAutoStartup()方法返回的布尔值。如果为真,则该对象在该点启动,而不是等待上下文的start() me的显式调用
SmartLifecycle
接口中有两个返回值为boolean
的方法:
-
isAutoStartup()
方法:在容器刷新的时候,会检查每个实现了SmartLifecycle
接口bean
的isAutoStartup()
方法的返回值,如果为true
,则会调用start()
方法。 - **
isRunning()
:**容器在销毁时会检测isRunning()
方法的返回值,如果为true
,则会调用stop()
方法。
1.2.4 在非web应用中优雅的关闭Spring容器
在基于Spring
的web
应用中,Spring
已经为我们写好了优雅的关闭容器的代码,我们不需要操心。
在基于Spring
的非web
应用中,我们需要向JVM
注册一个关闭钩子。这样做可以确保适当的关机,并调用singleton
作用域的bean
上的相关销毁方法,以便释放所有资源。你仍然必须正确配置和实现这些销毁回调。
要注册一个关闭钩子,请调用registerShutdownHook()
方法,该方法是在ConfigurableApplicationContext
接口上声明的,如下面的示例所示:
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Boot {
public static void main(final String[] args) throws Exception {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// 注册一个关闭钩子
ctx.registerShutdownHook();
}
}