Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.6 自定义Bean的特性 6.6.1 生命周期回调

6.6自定义Bean的特性

6.6.1生命周期回调

要与容器的bean生命周期管理进行交互,可以实现Spring的InitializingBean和DisposableBean接口。对于前者,容器会调用afterPropertiesSet(),对于后者,容器会调用destroy()以允许bean在初始化和销毁bean时执行某些操作。

注意:JSR-250 @PostConstruct和@PreDestroy注解通常被认为是在现代Spring应用程序中接受生命周期回调的最佳实践。使用这些注解意味着您的bean不会与Spring特定的接口耦合。 有关详细信息,请参见第6.9.8节“@PostConstruct和@PreDestroy”。
如果您不想使用JSR-250注解但仍希望删除耦合,请考虑使用init-method和destroy-method对象定义元数据。

在内部,Spring框架使用BeanPostProcessor实现来处理它可以找到的任何回调接口并调用适当的方法。如果您需要自定义功能或其他生命周期行为,Spring不提供开箱即用的功能,您可以自己实现BeanPostProcessor。 有关更多信息,请参见第6.8节“容器扩展点”。

除了初始化和销毁回调之外,Spring管理的对象还可以实现Lifecycle接口,以便这些对象可以参与由容器自身生命周期驱动的启动和关闭过程。

本节描述了生命周期回调接口。

初始化方法的回调

org.springframework.beans.factory.InitializingBean接口允许bean在容器设置bean的所有必要属性后执行初始化工作。InitializingBean接口指定了一个方法:

void afterPropertiesSet() throws Exception;

建议您不要使用InitializingBean接口,因为它会不必要地将代码耦合到Spring。不过使用@PostConstruct注解或指定POJO初始化方法会更好。在基于XML的配置元数据的情况下,可以使用init-method属性指定具有void无参数签名的方法的名称。在Java配置的情况下,可以使用@Bean的initMethod属性,请参阅“接受生命周期回调”一节。比如,以下:

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {

    public void init() {
        // do some initialization work
    }

}

…其实跟下面效果是一样的……

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {

    public void afterPropertiesSet() {
        // do some initialization work
    }

}

但是没有与Spring耦合的代码

销毁方法的回调

实现org.springframework.beans.factory.DisposableBean接口允许bean在包含它的容器中被销毁的时候获得回调。DisposableBean接口指定一个方法:

void destroy() throws Exception;

建议您不要使用DisposableBean回调接口,因为它会不必要地将代码耦合到Spring。不过使用@PreDestroy注解或指定bean定义支持的泛型方法会是更好的选择。基于XML的配置元数据,可以在<bean />上使用destroy-method属性。基于Java配置,您可以使用@Bean的destroyMethod属性,请参阅“接收生命周期回调”一节。 例如,以下定义:

<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耦合的代码
注意:可以为<bean>元素的destroy-method属性分配一个特殊的(推断的)值,该值指示Spring自动检测特定bean类(任何实现java.lang.AutoCloseable或java.io.Closeabler的类)的公共close或shutdown方法。 io.Closeable因此匹配)。 此特殊(推断)值也可以在<beans>元素的default-destroy-method属性上设置,以将此行为应用于整个bean集(请参阅“默认初始化和销毁方法”一节)。请注意,这是Java配置的默认行为。

默认初始化和销毁方法

当您在编写初始化和销毁回调方法,而不使用Spring指定的InitializingBean和DisposableBean回调接口的时候,通常,你会编写名称为init(),initialize(),dispose()等的方法。理想情况下,此类生命周期回调方法的名称在项目中是标准化的,以便所有开发人员使用相同的方法名称以确保一致性。

您可以配置Spring容器,以便在每个bean上查找名为初始化和销毁的回调方法。这意味着,作为应用程序开发人员,您可以编写应用程序类并使用名为init()的初始化回调方法,而无需为每个bean定义配置init-method =“init”属性。 Spring IoC容器在创建bean时调用该方法(并且符合前面描述的标准生命周期回调契约)。此特性还强制要求初始化和销毁回调方法命名的一致性。

假设您的初始化回调方法名为init(),而destroy回调方法名为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方法识别为初始化回调方法。当bean被创建和组装时,如果bean类具有这样的方法,则在适当的时候会调用它。

同样地,你可以通过在顶级<beans />元素上使用default-destroy-method属性来配置销毁回调方法。

如果在存在的bean的类中已经有与约定命名不一致的回调方法,则可以通过使用<bean />本身的init-method和destroy-method属性指定(在XML中,即)方法名称来覆盖缺省值。

Spring容器保证在为bean提供所有依赖项后立即调用已配置的初始化回调。因此,在原始bean引用上调用初始化回调方法,这意味着AOP拦截器等还未应用在bean上面。首先,完全创建目标bean,然后,应用具有其拦截器链的AOP代理(例如)。如果目标bean和代理是分开定义的,那么您的代码甚至可以绕过代理与原始目标bean进行交互。因此,将拦截器应用于init方法是不一致的,因为这样做会将目标bean的生命周期与其代理/拦截器耦合在一起,并在代码直接与原始目标bean进行交互时会留下奇怪的语义。

组合生命周期机制

从Spring 2.5开始,您有三个控制bean生命周期行为的选项:InitializingBean和DisposableBean回调接口; 自定义init()和destroy()方法; 以及@PostConstruct和@PreDestroy注释。 您可以组合这些机制来控制给定的bean。

注意:如果为bean配置了多个生命周期机制,并且每个机制配置了不同的方法名称,则每个配置的方法都按下面列出的顺序执行。但是,如果为初始化方法配置了相同的方法名称——例如,init()作为初始化方法——对于这些生命周期机制不止一个的情况下,该方法如一节中所述,只会执行一次。

为同一个bean配置的多个生命周期机制具有不同的初始化方法,会按如下所示的顺序被调用:

  • @PostConstruct注解标记的方法
  • 通过InitializingBean回调接口定义的afterPropertiesSet()方法
  • 自定义配置init()方法

销毁方法会按同样的顺序调用:

  • @PreDestroy注解标记的方法
  • 通过DisposableBean回调接口定义的destroy()方法
  • 自定义配置destroy()方法
启动和关闭回调方法

Lifecycle接口为任何具有自己生命周期要求的对象定义基本方法(例如,启动和停止某些后台进程):

public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();

}

任何Spring管理的对象都可以实现该接口。然后,当ApplicationContext本身接收到开始和停止信号时,例如,对于运行时的停止/重新启动方案,它会将这些调用级联到该上下文中定义的所有生命周期实现。它通过委托给LifecycleProcessor来实现:

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();

}

请注意,LifecycleProcessor本身是Lifecycle接口的扩展。它还添加了另外两种方法来响应刷新和关闭的上下文。

请注意,常规org.springframework.context.Lifecycle接口只是显式启动/停止通知的简单合约,并不意味着在上下文刷新时自动启动。 考虑实现org.springframework.context.SmartLifecycle,以便对特定bean的自动启动进行细粒度控制(包括启动阶段)。 此外,请注意,在销毁之前不能保证停止通知的发生:常规关闭时,在常规的销毁回调方法被传播之前,所有生命周期的bean将首先收到停止通知; 然而,在上下文生命周期中的热刷新或中止刷新尝试期间,只会调用destroy方法。

启动和关闭调用的顺序非常重要。如果任何两个对象之间存在“depends-on”关系,则依赖方将在其依赖之后启动,并且它将在其依赖之前停止。然而,有时直接依赖是未知的。您可能只知道某种类型的对象应该在另一种类型的对象之前开始。在这些情况下,SmartLifecycle接口定义了另一个选项,即在其超级接口Phased上定义的getPhase()方法。

public interface Phased {

    int getPhase();

}
public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();

    void stop(Runnable callback);

}

启动时,具有最低相位的对象首先开始,停止时,遵循相反的顺序。 因此,实现SmartLifecycle以及具有返回Integer.MIN_VALUE的getPhase()方法的对象将是第一个开始和最后一个停止的对象。在范围的另一端,Integer.MAX_VALUE的阶段值将指示对象应该最后启动并首先停止(可能是因为它依赖于正在运行的其他进程)。在考虑相位值时,同样重要的是要知道任何未实现SmartLifecycle的“正常”生命周期对象的默认阶段都是0。因此,任何负相位值都表示对象应该在这些标准组件之前启动(和 在他们之后停止),对于任何正相位值,反之亦然。

如您所见,SmartLifecycle定义的stop方法接受回调。 在该实现的关闭过程完成之后,任何实现都必须调用该回调的run()方法。这样就可以在必要时启用异步关闭,因为LifecycleProcessor接口的默认实现DefaultLifecycleProcessor将等待每个阶段内的对象组的超时,再来调用该回调。默认的每阶段超时为30秒。您可以通过在上下文中定义名为“lifecycleProcessor”的bean来覆盖缺省生命周期处理器实例。如果您只想修改超时,那么定义以下内容就足够了:

<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()方法返回的布尔值。如果为“true”,则该对象将在该点启动,而不是等待显式调用上下文或其自己的start()方法(与上下文刷新不同,上下文启动不会自动发生在标准上下文实现中) 。 “阶段”值以及任何“依赖”关系将以与上述相同的方式确定启动顺序。

在非Web应用程序中优雅地关闭Spring IoC容器

注意:本节仅适用于非Web应用程序。 Spring的基于Web的ApplicationContext实现已经有代码可以在关闭相关Web应用程序时正常关闭Spring IoC容器。

如果您在非Web应用程序环境中使用Spring的IoC容器; 例如,在富客户端桌面环境中; 您在JVM上注册了一个关闭钩子。 这样做可确保正常关闭并在单例bean上调用相关的destroy方法,以便释放所有资源。当然,您仍然必须正确配置和实现这些destroy回调方法。

要注册一个关闭钩子,请调用ConfigurableApplicationContext接口上声明的registerShutdownHook()方法:

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(
                new String []{"beans.xml"}); // add a shutdown hook for the above context...  ctx.registerShutdownHook(); // app runs here... // main method exits, hook is called prior to the app shutting down...  } }

猜你喜欢

转载自www.cnblogs.com/springmorning/p/10354408.html
今日推荐