参考:官方文档

1 Spring框架中的生命周期回调

Spring框架提供了一些接口,能够让bean感知生命周期。

1.1 Bean的生命周期回调

Spring容器提供了两个接口可以让容器管理的bean感知生命周期:

  • InitializingBean:提供了一个回调函数afterPropertiesSet(),在bean初始化的时候被调用。
  • DisposableBean:提供了一个回调函数destroy(),在bean被销毁之前被调用。

JSR-250提供了@PostConstruct@PreDestroy注解实现相同的功能。

如果在xml中配置bean,可以使用init-methoddestroy-method实现相同的功能。

Spring内部,Spring框架使用的是BeanPostProcessor的实现类来处理任何可以找到的生命周期回调函数。如果想定制Spring提供的生命周期回调以外的回调函数,可以自己实现BeanPostProcessor接口。更多的信息可以参考Container Extension Points。

除了前边的两个回调,Spring还提供了Lifecycle接口,可以让bean 感知容器的生命周期。

注意:

InitializingBeanDisposableBeanLifecycle的不同点在于:

  • InitializingBeanDisposableBean提供的回调是建立在Spring容器已经启动完毕的前提之下,它们提供的回调函数只能让bean感知自己被初始化和销毁的生命周期。
  • Lifecycle接口提供了让bean去感知Spring容器本身的启动和停止生命周期的能力。

1.1.1 初始化时的回调

Spring提供的org.springframework.beans.factory.InitializingBean接口,允许beanSpring容器为它设置完毕所有有效的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接口beanisAutoStartup() 方法的返回值,如果为true,则会调用start()方法。
  • **isRunning():**容器在销毁时会检测isRunning()方法的返回值,如果为true,则会调用stop()方法。

1.2.4 在非web应用中优雅的关闭Spring容器

在基于Springweb应用中,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();
    }
}