【翻译计划】-Spring Framework Documentation(六)

原文链接:docs.spring.io/spring-fram…

1.6 定制化Bean

Spring框架提供了许多接口,您可以使用它们来对bean进行定制化。本节将它们分组如下:

1.6.1 生命周期回调(Lifecycle Callbacks)

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

JSR-250 @PostConstruct和@PreDestroy注解通常被认为是现代Spring应用程序中接收生命周期回调的最佳实践。使用这些注解意味着您的bean没有与特定的spring接口进行耦合。详细信息请参见使用@PostConstruct和@PreDestroy docs.spring.io/spring-fram…

如果您不想使用JSR-250注释,但仍然希望删除耦合,可以考虑init-method和destroy-method。

在内部,Spring框架使用BeanPostProcessor实现来处理它能找到的任何回调接口,并调用适当的方法。如果您需要自定义特性或Spring默认不提供的其他生命周期行为,您可以自己实现BeanPostProcessor。有关更多信息,请参见 Container Extension Points. docs.spring.io/spring-fram…

除了初始化和销毁回调,spring管理的对象也可以实现Lifecycle接口,以便这些对象可以参与容器启动和关闭过程

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

初始化回调(Initialization Callbacks)

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

void afterPropertiesSet() throws Exception;
复制代码

我们建议您不要使用InitializingBean接口,因为它不必要地将代码耦合到Spring。另外,我们建议使用@PostConstruct注释或指定POJO初始化方法。对于基于xml的配置元数据,可以使用init-method属性指定空参数的方法为初始化方法。基于Java配置,您可以使用@Bean的initMethod属性。 Receiving Lifecycle Callbacks。考虑以下例子:

<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 {

    @Override
    public void afterPropertiesSet() {
        // do some initialization work
    }
}
复制代码

但是,前面两个示例中的第一个没有将代码与Spring耦合。

销毁回调方法(Destruction Callbacks)

实现org.springframework. factory. disposablebean接口可以让bean在容器销毁它时获得一个回调。DisposableBean接口指定了一个方法:

void destroy() throws Exception;
复制代码

我们建议您不要使用DisposableBean回调接口,因为它不必要地将代码与Spring耦合在一起。另外,我们建议使用@PreDestroy注释或使用基于xml的配置元数据,使用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 {

    @Override
    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}
复制代码

但是,前面两个示例中的第一个没有将代码与Spring耦合。

默认的初始化、 销毁回调方法

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

您可以将Spring容器配置“查找”已命名初始化或销毁的回调方法。这意味着,作为应用程序开发人员,您可以编写应用程序类并使用名为init()的初始化回调函数,而不必为每个bean定义配置init-method=”init”属性。Spring IoC容器在创建bean时调用该方法(并且按照前面描述的标准生命周期回调契约)。
假设初始化回调方法名为init(),销毁回调方法名为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.");
    }
}
复制代码

然后你可以像下面这样在bean中使用这个类:

<bean id="blogService" class="com.something.DefaultBlogService">
    <property name="blogDao" ref="blogDao" />
</bean>
复制代码

顶级元素属性上的default-init-method属性导致Spring IoC容器将bean类上名为init的方法识别为初始化方法回调。在创建和组装一个bean时,如果这个bean类有这样的方法,就会在适当的时候调用它。

通过在顶级元素上使用default-destroy-method属性,您可以类似地配置销毁方法回调。

如果现有的bean类已经有了与约定不同的回调方法,那么您可以通过使用本身的init-method和destroy-method属性指定方法名来覆盖默认值。

Spring容器保证在装载了包含所有依赖项的bean之后立即调用配置好的初始化回调。

复合生命周期机制

从spring2.5开始,你有三个选项来控制bean的生命周期行为:

你可以复合使用这些机制。

如果为一个bean配置了多个生命周期机制,并且每个机制都配置了不同的方法名,那么每个已配置的方法将按照本文后面列出的顺序运行。但是,如果为多个生命周期机制配置了相同的方法名——例如,为一个初始化方法配置init(),则该方法将运行一次

为同一个bean配置的具有不同初始化方法的多个生命周期机制的调用如下:

  • 调用用@PostConstruct注释的方法

  • 调用InitializingBean回调接口定义的afterPropertiesSet()

  • 调用自定义配置的init()方法

销毁方法也遵循以下顺序:

  1. 调用 @PreDestroy注释的方法
  2. 调用DisposableBean 回调接口定义的destroy() 方法
  3. 自定义destroy() 方法

Startup 、 Shutdown 回调方法

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

public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();
}
复制代码

任何spring管理的对象都可以实现Lifecycle接口。然后,当ApplicationContext本身接收到启动和停止信号(例如,对于运行时的停止/重启场景)时,它将这些调用级联到该上下文中定义的所有实现Lifecycle接口的对象。它通过委托给LifecycleProcessor来做到这一点,如下所示:

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();
}
复制代码

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

请注意,常规的org.springframework.context.Lifecycle接口是显式启动和停止通知的简单约定,并不意味着在上下文刷新时自动启动。对于特定bean的自动启动(包括启动阶段)的细粒度控制,可以考虑实现org.springframework.context.SmartLifecycle。

另外,请注意,停止通知不能保证在销毁之前出现。在常规关闭时,在传播常规销毁回调之前,所有Lifecycle bean首先收到一个停止通知。但是,在上下文生命周期内的热刷新或停止刷新尝试时,只调用destroy销毁方法。

启动和关闭调用的顺序可能很重要。如果任意两个对象之间存在“依赖”关系,依赖方在其依赖项之后启动,在其依赖项之前停止。然而,有时,直接依赖关系是未知的。您可能只知道某种类型的对象应该在另一种类型的对象之前启动。在这些情况下,SmartLifecycle接口定义了另一个选项,即在其父接口phase上定义的getPhase()方法。phase接口的定义如下所示:

public interface Phased {

    int getPhase();
}
复制代码

下面的列表显示了SmartLifecycle接口的定义:

public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();

    void stop(Runnable callback);
}
复制代码

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

SmartLifecycle定义的stop方法接受回调。任何实现都必须在实现的shutdown()完成后调用回调的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接口还定义了用于刷新(onrefresh)和关闭(onclose)上下文的回调方法。后者驱动关机进程,就像显式调用了stop()一样,但它发生在上下文关闭时。另一方面,’refresh’回调启用了SmartLifecycle bean的另一个特性。当上下文被刷新时(在所有对象被实例化和初始化之后),该回调将被调用。此时,默认的生命周期处理器会检查每个SmartLifecycle对象的isAutoStartup()方法返回的布尔值。如果为true,则该对象将在此点启动,而不是等待上下文的显式调用或它自己的start()方法(不同于上下文刷新,对于标准上下文实现,上下文启动不会自动发生)。如前所述,parse值和任何“依赖”关系决定启动顺序。

在非web应用程序中安全地关闭Spring IoC容器

本节仅适用于非web应用。Spring基于web的ApplicationContext实现如上所述,可以在相关web应用程序关闭时优雅地关闭Spring IoC容器。

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

要注册一个shutdown钩子,调用在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("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...
}
复制代码

}`

1.6.2. ApplicationContextAware and BeanNameAware

当ApplicationContext创建一个实现org.springframework.context.ApplicationContextAware接口的对象实例时,该实例会提供ApplicationContext的引用。下面代码显示了ApplicationContextAware接口的定义:

public interface ApplicationContextAware {
    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
复制代码

因此,bean可以通过程序操作创建它们的ApplicationContext,通过ApplicationContext接口,或者通过将引用强转成该接口的已知子类(例如提供了额外功能的ConfigurableApplicationContext)。一种用途是对其他bean进行检索。有时这种能力是有用的,但是,通常应该避免使用它,因为它将代码耦合到Spring,并且不遵循控制反转(Inversion of Control),在这种样式中协作者作为属性提供给bean。ApplicationContext的其他方法提供了对文件资源的访问、发布应用程序事件和访问MessageSource。这些附加特性在ApplicationContext的附加功能中进行了描述。

自动装配是另一种获取ApplicationContext引用的方法。传统的构造函数和byType自动装配模式(如autowiring合作者所述)可以分别为构造函数参数或setter方法参数提供ApplicationContext类型的依赖关系。为了获得更多的灵活性,包括自动装配字段和多个参数方法的能力,可以使用基于注释的自动装配特性。如果您这样做,ApplicationContext将自动连接到一个字段、构造函数参数或方法参数,如果相关字段、构造函数或方法带有@Autowired注释,则该字段、构造函数或方法参数需要为ApplicationContext类型。有关更多信息,请参见使用@Autowired

当ApplicationContext创建一个实现org.springframework.beans.factory.BeanNameAware接口的类时,会为该类提供一个对对象定义中定义的名称。下面的代码显示了BeanNameAware接口的定义:

public interface BeanNameAware {

    void setBeanName(String name) throws BeansException;
}
复制代码

回调在填充普通bean属性之后,但在初始化回调(如InitializingBean、propertiesset之后或自定义初始化方法之前)之前被调用。

1.6.3. Other Aware Interfaces

除了ApplicationContextAware和BeanNameAware(前面讨论过)之外,Spring还提供了一系列Aware回调接口,让bean向容器表明它们需要的特定的依赖项。作为一般规则,Aware前的名称表示依赖项。下表总结了最重要的Aware接口:

Name Injected Dependency Explained in…
ApplicationContextAware Declaring ApplicationContext. ApplicationContextAware and BeanNameAware
ApplicationEventPublisherAware Event publisher of the enclosing ApplicationContext. Additional Capabilities of the ApplicationContext
BeanClassLoaderAware Class loader used to load the bean classes. Instantiating Beans
BeanFactoryAware Declaring BeanFactory. ApplicationContextAware and BeanNameAware
BeanNameAware Name of the declaring bean. ApplicationContextAware and BeanNameAware
LoadTimeWeaverAware Defined weaver for processing class definition at load time. Load-time Weaving with AspectJ in the Spring Framework
MessageSourceAware Configured strategy for resolving messages (with support for parametrization and internationalization). Additional Capabilities of the ApplicationContext
NotificationPublisherAware Spring JMX notification publisher. Notifications
ResourceLoaderAware Configured loader for low-level access to resources. Resources
ServletConfigAware Current ServletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext. Spring MVC
ServletContextAware Current ServletContext the container runs in. Valid only in a web-aware Spring ApplicationContext. Spring MVC
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享