Spring文档之自定义 Bean 的性质

2021/10/11 6:14:21

本文主要是介绍Spring文档之自定义 Bean 的性质,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录
  • 2.6.1. 生命周期回调
    • 初始化回调
    • 销毁回调
    • 默认初始化和销毁方法
    • 结合生命周期机制
    • 启动和关闭回调
    • 在非 Web 应用程序中优雅地关闭 Spring IoC 容器
  • 2.6.2. ApplicationContextAware和BeanNameAware
  • 2.6.3. 其他Aware接口

Spring Framework 提供了许多可用于自定义 bean 性质的接口。本节将它们分组如下:

  • 生命周期回调
  • ApplicationContextAwareBeanNameAware
  • 其他Aware接口

2.6.1. 生命周期回调

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

注意:

JSR-250 @PostConstruct和@PreDestroy注释通常被认为是在现代Spring应用程序中接收生命周期回调的最佳实践。使用这些注释意味着您的bean没有耦合到spring特定的接口。具体请参见使用@PostConstruct和@PreDestroy。

如果不想使用JSR-250注释,但仍然希望消除耦合,请考虑init-method和destroy-method bean定义元数据。

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

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

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

初始化回调

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

void afterPropertiesSet() throws Exception;

我们建议您不要使用InitializingBean接口,因为它不必要地将代码与Spring耦合在一起。或者,我们建议使用@PostConstruct注释或指定POJO初始化方法。在基于xml的配置元数据的情况下,可以使用init-method属性指定具有无效无参数签名的方法的名称。在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 {

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

然而,前面两个示例中的第一个并没有将代码与Spring耦合起来。

销毁回调

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

void destroy() throws Exception;

我们建议您不要使用DisposableBean回调接口,因为它不必要地将代码与Spring耦合在一起。
另外,我们建议使用@PreDestroy注释或指定bean定义支持的泛型方法。对于基于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。

注意:

您可以为元素的destroy-method属性指定一个特殊的(推断的)值,该值指示Spring自动检测特定bean类上的公共关闭或关闭方法。(因此,任何实现java.lang.AutoCloseable或java.io.Closeable的类都可以匹配。)您还可以在元素的Default - Destroy -method属性上设置这个特殊的(推断的)值,以便将此行为应用到整个bean集(请参阅默认初始化和Destroy方法)。
注意,这是Java配置的默认行为。

默认初始化和销毁方法

当编写不使用特定于spring的InitializingBeanDisposableBean回调接口的初始化和销毁方法回调时,通常编写具有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.");
        }
    }
}

然后,您可以在bean中使用该类,类似如下:

<beans default-init-method="init">

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

</beans>

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

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

如果现有的bean类已经具有按约定命名的回调方法,那么您可以通过使用本身的init-method和destroy-method属性指定(在XML中)方法名称来覆盖默认值。

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

结合生命周期机制

从 Spring 2.5 开始,您可以通过三个选项来控制 bean 生命周期行为:

  • InitializingBeanDisposableBean回调接口
  • 自定义init()destroy()方法
  • @PostConstruct@PreDestroy 注释。您可以组合这些机制来控制给定的 bean。

注意:

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

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

  1. 带有注释的方法 @PostConstruct
  2. afterPropertiesSet()InitializingBean回调接口定义
  3. 自定义配置init()方法

销毁方法以相同的顺序调用:

  1. 带有注释的方法 @PreDestroy
  2. destroy()DisposableBean回调接口定义
  3. 自定义配置destroy()方法
启动和关闭回调

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

public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();
}

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

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();
}

注意,LifecycleProcessor本身就是Lifecycle接口的扩展。它还添加了另外两个方法,用于对正在刷新和关闭的上下文作出反应。

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

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

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

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表示该对象应该最后启动,最先停止(可能是因为它依赖于其他正在运行的进程)。当考虑阶段值时,知道任何不实现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(),但它发生在上下文关闭时。
另一方面,‘refresh’回调启用SmartLifecycle bean的另一个特性。
当上下文刷新时(在所有对象都已实例化和初始化之后),将调用该回调。
此时,默认的生命周期处理器检查每个SmartLifecycle对象的isAutoStartup()方法返回的布尔值。
如果为true,则该对象将在此时启动,而不是等待显式调用上下文或它自己的start()方法(与上下文刷新不同,对于标准上下文实现,上下文启动不会自动发生)。
如前所述,阶段值和任何“依赖”关系决定启动顺序。

在非 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...
    }
}

2.6.2. ApplicationContextAwareBeanNameAware

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

public interface ApplicationContextAware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

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

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

当ApplicationContext创建一个实现了org.springframework.beans.factory.BeanNameAware接口的类时,这个类将被提供一个对其关联对象定义中定义的名称的引用。
下面的清单显示了BeanNameAware接口的定义:

public interface BeanNameAware {

    void setBeanName(String name) throws BeansException;
}

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

2.6.3. 其他Aware接口

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

Name 注入依赖 在…中解释
ApplicationContextAware 声明ApplicationContext. ApplicationContextAwareBeanNameAware
ApplicationEventPublisherAware 封闭的事件发布者ApplicationContext 附加功能 ApplicationContext
BeanClassLoaderAware 类加载器用于加载 bean 类。 实例化 Bean
BeanFactoryAware 声明BeanFactory. BeanFactory
BeanNameAware 声明 bean 的名称。 ApplicationContextAwareBeanNameAware
LoadTimeWeaverAware 用于在加载时处理类定义的定义编织器。 在 Spring 框架中使用 AspectJ 进行加载时编织
MessageSourceAware 用于解析消息的配置策略(支持参数化和国际化)。 附加功能 ApplicationContext
NotificationPublisherAware Spring JMX 通知发布者。 通知
ResourceLoaderAware 为低级访问资源配置加载器。 资源
ServletConfigAware 当前ServletConfig容器运行。仅在 web-aware Spring 中有效ApplicationContext Spring MVC
ServletContextAware 当前ServletContext容器运行。仅在 web-aware Spring 中有效ApplicationContext Spring MVC

再次注意,使用这些接口将您的代码与Spring API绑定在一起,而不是遵循控制反转风格。
因此,我们将它们推荐给需要对容器进行编程访问的基础架构bean。



这篇关于Spring文档之自定义 Bean 的性质的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程