5.spring framework 5.2.3.RELEASE 文档之核心技术(Core Technologies) 第四篇 自定义bean原生特性

目录

 

1.6. 自定义bean原生特性

1.6.1. 生命周期回调(Lifecycle Callbacks)

1.6.2. ApplicationContextAware 和 BeanNameAware

1.6.3. 其它Aware 接口


1.6. 自定义bean原生特性

Spring框架提供了许多接口,您可以使用这些接口来定制bean的性质。本节将它们分组如下:

1.6.1. 生命周期回调(Lifecycle Callbacks)

要与容器对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接口,以便这些对象可以参与启动和关闭过程,由容器自身的生命周期驱动。

本节介绍了Lifecycle回调接口。

初始化回调

使用org.springframework.beans.factory.InitializingBean接口,容器在容器上设置了所有必需的属性后,就可以执行初始化工作。 InitializingBean接口指定一个方法:

java 示例代码

void afterPropertiesSet() throws Exception;

kotlin示例代码

fun afterPropertiesSet()

我们建议不要使用InitializingBean接口,因为它不必要地将代码耦合到Spring。或者,我们建议使用@PostConstruct注解或指定POJO初始化方法。对于基于XML的配置元数据,可以使用init method属性指定具有void no argument签名的方法的名称。使用Java配置,可以使用@Bean的initMethod属性。请参阅接收生命周期回调。请参考以下示例:

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

java 示例代码

public class ExampleBean {

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

kotlin示例代码

class ExampleBean {

    fun init() {
        // do some initialization work
    }
}

前面的示例与下面的示例(由两个列表组成)具有几乎完全相同的效果:

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>

java 示例代码

public class AnotherExampleBean implements InitializingBean {

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

kotlin示例代码

class AnotherExampleBean : InitializingBean {

    override fun afterPropertiesSet() {
        // do some initialization work
    }
}

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

销毁回调

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

java 示例代码

void destroy() throws Exception;

kotlin示例代码

fun destroy()

我们建议不要使用DisposableBean回调接口,因为它不必要地将代码耦合到Spring。或者,我们建议使用@PreDestroy注解或指定bean定义支持的泛型方法。使用基于XML的配置元数据,可以在<bean/>上使用destroy method属性。使用Java配置,可以使用@Bean的destroyMethod属性。请参阅接收生命周期回调。参考以下定义:

<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>

java 示例代码

public class ExampleBean {

    public void cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}

kotlin示例代码

class ExampleBean {

    fun cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}

上述定义与以下定义具有几乎完全相同的效果:

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>

java 示例代码

public class AnotherExampleBean implements DisposableBean {

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

kotlin示例代码

class AnotherExampleBean : DisposableBean {

    override fun destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}

但是,前面两个定义中的第一个并没有将代码耦合到Spring。

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

默认初始化和销毁方法

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

您可以将Spring容器配置为“look”命名的初始化,并销毁每个bean上的回调方法名称。 这意味着,作为应用程序开发人员,您可以编写应用程序类并使用称为init()的初始化回调,而不必为每个bean定义配置init-method ="init"属性。 当创建bean时,Spring IoC容器调用该方法(并按照前面描述的标准生命周期回调协定)。 此功能还对初始化和销毁方法回调强制执行一致的命名约定。

假设您的初始化回调方法命名为init(),而destroy回调方法命名为destroy()。 然后,您的类类似于以下示例中的类:

java 示例代码

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.");
        }
    }
}

kotlin示例代码

class DefaultBlogService : BlogService {

    private var blogDao: BlogDao? = null

    // this is (unsurprisingly) the initialization callback method
    fun init() {
        if (blogDao == null) {
            throw 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>

顶级<beans />元素属性上存在default-init-method属性,导致Spring IoC容器将Bean类上称为init的方法识别为初始化方法回调。 创建和组装bean时,如果bean类具有这种方法,则会在适当的时间调用它。

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

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

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

组合生命周期机制

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

如果为一个bean配置了多个生命周期机制,并且为每个机制配置了不同的方法名称,则将按照此注解后列出的顺序执行每个已配置的方法。 但是,如果为多个生命周期机制中的多个生命周期配置了相同的方法名称(例如,为初始化方法使用init()),则该方法将执行一次,如上一节所述。

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

  1. 为方法添加 @PostConstruct 注解

  2. 由InitializingBean回调接口定义的afterPropertiesSet()

  3.  自定义配置 init() 方法

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

  1. 为方法添加 @PreDestroy注解

  2. 由DisposableBean 回调接口定义的destroy()

  3. 自定义配置 destroy() 方法

启动和关闭回调

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

java 示例代码

public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();
}

kotlin示例代码

interface Lifecycle {

    fun start()

    fun stop()

    val isRunning: Boolean
}

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

java 示例代码

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();
}

kotlin示例代码

interface LifecycleProcessor : Lifecycle {

    fun onRefresh()

    fun onClose()
}

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

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

 

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

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

java 示例代码

public interface Phased {

    int getPhase();
}

kotlin示例代码

interface Phased {

    val phase: Int
}

以下清单显示了SmartLifecycle接口的定义:

java 示例代码

public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();

    void stop(Runnable callback);
}

kotlin示例代码

interface SmartLifecycle : Lifecycle, Phased {

    val isAutoStartup: Boolean

    fun stop(callback: Runnable)
}

启动时,相位最低的对象先启动。停此时,按相反的顺序行驶。因此,实现SmartLifecycle且其getPhase()方法返回Integer.MIN_值的对象将是最先开始和最后停止的对象之一。在频谱的另一端,Integer.MAX_value的相位值将指示对象应最后启动并首先停止(可能是因为它依赖于要运行的其他进程)。在考虑阶段值时,还必须知道任何未实现SmartLifecycle的“正常”生命周期对象的默认阶段为0。因此,任何负相位值都表示对象应该在这些标准组件之前开始(并在它们之后停止)。对于任何正相位值,反之亦然。

SmartLifecycle定义的stop方法接受回调。任何实现都必须在该实现的关闭过程完成后调用该回调的run()方法。这将在必要时启用异步关闭,因为LifecycleProcessor接口的默认实现DefaultLifecycleProcessor将等待每个阶段中的对象组调用该回调的超时值。每个阶段的默认超时为30秒。通过在上下文中定义一个名为lifecycle processor的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注册一个关闭挂钩。这样做可以确保正常关机,并调用singleton bean上的相关销毁方法,以便释放所有资源。您仍然必须正确配置和实现这些销毁回调。

要注册关闭挂钩,请调用在ConfigurableApplicationContext接口上声明的registerShutdownHook()方法,如以下示例所示:

java 示例代码

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

kotlin示例代码

import org.springframework.context.support.ClassPathXmlApplicationContext

fun main() {
    val ctx = 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 和 BeanNameAware

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

java 示例代码

public interface ApplicationContextAware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

kotlin示例代码

interface ApplicationContextAware {

    @Throws(BeansException::class)
    fun setApplicationContext(applicationContext: ApplicationContext)
}

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

自动装配是获得对ApplicationContext的引用的另一种方法。 传统的构造函数和byType自动装配模式(如“自动装配协作器”中所述)可以分别为构造函数参数或setter方法参数提供ApplicationContext类型的依赖项。 要获得更大的灵活性,包括能够自动连接字段和使用多个参数方法,请使用基于注解的自动装配功能。 如果这样做,则将ApplicationContext自动连接到需要使用ApplicationContext类型的字段,构造函数参数或方法参数中(如果有问题的字段,构造函数或方法带有@Autowired注解)。 有关更多信息,请参见使用@Autowired。

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

java 示例代码

public interface BeanNameAware {

    void setBeanName(String name) throws BeansException;
}

kotlin示例代码

interface BeanNameAware {

    @Throws(BeansException::class)
    fun setBeanName(name: String)
}

回调在正常bean属性填充之后调用,但在初始化回调(如InitializingBean、afterPropertiesSet或自定义init方法)之前调用。

1.6.3. 其它Aware 接口

除了ApplicationContextAware和BeanNameAware(前面已经讨论过)之外,Spring还提供了多种Aware回调接口,这些接口使Bean向容器指示它们需要某种基础结构依赖性。 通常,名称表示依赖项类型。 下表总结了最重要的Aware接口:

Table 4. Aware interfaces

NameInjected DependencyExplained in…​
ApplicationContextAwareDeclaring ApplicationContext.ApplicationContextAware and BeanNameAware
ApplicationEventPublisherAwareEvent publisher of the enclosing ApplicationContext.Additional Capabilities of the ApplicationContext
BeanClassLoaderAwareClass loader used to load the bean classes.Instantiating Beans
BeanFactoryAwareDeclaring BeanFactory.ApplicationContextAware and BeanNameAware
BeanNameAwareName of the declaring bean.ApplicationContextAware and BeanNameAware
BootstrapContextAwareResource adapter BootstrapContext the container runs in. Typically available only in JCA-aware ApplicationContext instances.JCA CCI
LoadTimeWeaverAwareDefined weaver for processing class definition at load time.Load-time Weaving with AspectJ in the Spring Framework
MessageSourceAwareConfigured strategy for resolving messages (with support for parametrization and internationalization).Additional Capabilities of the ApplicationContext
NotificationPublisherAwareSpring JMX notification publisher.Notifications
ResourceLoaderAwareConfigured loader for low-level access to resources.Resources
ServletConfigAwareCurrent ServletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext.Spring MVC
ServletContextAwareCurrent ServletContext the container runs in. Valid only in a web-aware Spring ApplicationContext.Spring MVC

再次注意,使用这些接口会将您的代码与Spring API绑定在一起,并且不遵循“控制反转”样式。 因此,我们建议将它们用于需要以编程方式访问容器的基础结构Bean。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值