IoC容器篇(六)——定制Bean性质、Bean定义继承

目录

定制Bean的性质

1.生命周期回调函数

2.ApplicationContextAware 与 BeanNameAware

3.其他Aware接口

Bean定义继承

翻译源:Spring官方文档


定制Bean的性质

 

1.生命周期回调函数

可以通过实现Spring的InitializingBean与DisposableBean接口来与容器对bean生命周期的管理进行交互。

容器通过调用InitializingBean接口的afterPropertiesSet()方法在beans的initialization阶段执行特定的操作。

容器通过调用DisposableBean接口的destory()方法在beans的destruction阶段执行特定的操作。

 

JSR-250的 @PostConstruct、@PreDestroy注解通常被认为是在现在Spring应用中接收生命周期回调函数的最佳实践。

通过JSR的注解,可以使代码与Spring解耦和。

 

Spring框架内部使用BeanPostProcessor实现来运行其所能找到并调用的回掉接口中的合适方法。

如果需要定制Spring没有直接提供的特性或其他生命周期行为,可以实现一个自己的BeanPostProcessor。

 

除了initialization与destruction回调方法,Spring管理的对象还可以实现Lifecycle接口,使得这些对象可以如同被容器自身生命周期所驱动一般,参与startup与shutdown过程。

 

Initialization回调方法

void afterPropertiesSet() throws Exception;

note:此API在org.springframework.beans.factory.InitializingBean接口中定义。

note:此方法定义的初始化工作为在bean中所有必要属性已经被设置进入容器之后的初始化工作。

ps:由于与Spring耦合,并不推荐使用实现此接口的方法设置初始化行为。

ps:作为替代,可以使用@PostConstruct注解或基于xml配置指定一个初始化方法。

 

<bean id="example" class="examples.Example" init-method="init" />
public class Example {

    void init() {
        ...
    }

}

ps:在使用基于xml的配置元数据的情况下,通过使用init-method属性指定的一个void无参方法名称,令其作initialization回调方法。

 

Destruction回调方法

void destroy() throws Exception;

note:此API在org.springframework.beans.factory.DisposableBean接口中定义。

note:此方法为bean当持有它的容器被销毁时执行的回调。

ps:由于与Spring耦合,并不推荐使用此接口。

ps:作为替代,可以使用@PreDestroy注解或基于xml方法指定一个方法。

 

<bean id="example" class="examples.Example" destory-method="cleanup" />
package examples;
public class Example {
    
    public void cleanup() {
        ...
    }

}

 

默认initialization方法与destruction方法

 

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

    <bean id="example" class="examples.Example" />
</beans>
public class Example {

    public void init(){

    }
}

ps:通过设置顶层元素<beans>的default-init-method属性,IoC容器可以识别出所有bean中名为init的方法,并将其作为初始化回调函数。

ps:可以通过使用<beans>元素中的default-destroy-method属性设置销毁回调函数。

ps:可以使用<bean>元素中的init-method属性与destroy-method重载默认回调函数。

 

当bean的所有依赖都被提供之后,配置的初始化回调函数会被立刻调用。

因此,回调函数将会被不完全bean的引用调用,也就是说AOP拦截器等不能被应用于此bean。

当一个bean被完整创建之后,拦截器链上的AOP代理才可以被应用。

如果一个bean与AOP代理分开定义,那么代码可以通过绕开代理直接与不完全bean交互。

因此,将拦截器应用于初始化方法将会引发不一致,因为当代码直接与不完全目标bean交互时,这样做会将目标bean的生命周期与它的代理与拦截器连接,并且遗留奇怪的语法。

 

结合生命周期机制

Spring2.5之后,存在三种控制bean生命周期行为的方法:

InitializaingBean、DisposableBean回调函数接口

定制init()、destroy()方法

@PostConstruct、@PreDestroy注解

 

相同bean的不同名初始化函数调用顺序:

@PostConstruct注解的方法

InitializaingBean回掉函数接口定义的afterPropertiesSet()

定制init()方法

 

相同bean的不同名销毁函数调用顺序:

@PreDestroy注解的方法

DisposableBean回掉函数接口定义的destroy()

定制destroy()方法

 

startup与shutdown回调函数

Lifecycle接口定义了任何一个拥有属于自身生命周期的对象的基础方法

public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();
}

 

任何被Spring托管的对象都可以实现此接口。

当ApplicationContext自身接收start与stop信号,它将会级联调用定义在其中的Lifecycle接口实现。通过委托给LifecycleProcessor执行。

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();
}

ps:LifecycleProcessor自身使Lifecycle的一个扩展,增添了对context刷新关闭时的反应方法。

ps:onClose()回调方法驱动shutdown过程就像显式调用stop()方法一样,但是它在context关闭时调用。

ps:当context被刷新时(所有对象已经实例化并且初始化之后),onRefresh()回调方法将会被调用,并且在这个时候,默认lifecycle processor会自动检测所有实现了SmartLifecycle接口的对象的isAutoStartup()返回的boolean值。

ps:如果返回值为true,这个对象将会被启动,而不是等待context的显示调用,或它自身的start()方法。(与context刷新不同,一个标准context实现不会自动发生context start。)

ps:phase就如同"depends-on"关系决定了startup的顺序。

 

Lifecycle接口是一个简单的显式约定,用于显式地start/stop通知并且在context刷新时,不会指示auto-startup。

stop通知并不保证在销毁之前到达。

通常在关闭时,在销毁回调接口被传递之前,所有的lifecycle beans将会首先接收一个stop通知。

然而,在热刷新与放弃刷新的context生命周期中,只有destroy方法会被调用。

 

启动与关闭调用的顺序有时比较重要。

当"depends-on"关系存在于两个对象之间时,依赖方将会在依赖启动之后启动,在依赖关闭之前关闭。

如果只知道某些类型的对象比其他类型的对象启动要优先,SmartLifecycle提供了定义在超接口中的getPhase()方法作为另一种选择。

public interface Phased {

    int getPhase();
}
public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();

    void stop(Runnable callback);
}

ps:当开始时,最低阶段的对象将会首先开始,当结束时,上述顺序会反转。即,一个实现了SmartLifecycle接口并且其getPhase()方法返回Integer.MIN_VALUE的对象将会第一个开始,最后一个结束。

ps:一个没有实现Lifecycle接口的普通对象的phase为0。

ps:任何phrase值为负数的对象将会在标准组件之间开始,在其之后结束。

 

所有SmartLifecycle的实现都必须在实现的shutdown过程完成之后调用stop方法接受的回调接口的run方法。

这使得在有必要的地方进行异步shutdown,因为LifecycleProcessor的默认实现DefaultLifecycleProcessor为每一个阶段中对象组去调用回调接口等待设置的timeout值。

默认每个阶段的超时时间为30秒。

 

<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
    <!-- timeout value in milliseconds -->
    <property name="timeoutPerShutdownPhase" value="10000"/>
</bean>

note:重载lifecycle processor设置的超时值。

 

非web应用中平缓关闭Spring IoC容器

基于web的Spirng应用的ApplicationContext实现已经包含了,当web应用关闭时,适当平缓关闭容器的代码。

 

如果在非web应用环境下,通过注册一个关联JVM关闭的钩子。这样做确保了平缓关闭以及调用单例bean上关联的销毁方法,以此确保所有的资源都被释放。

 

通过调用ConfigurableApplicationContext的resgisterShutDownHook()方法,注册关闭钩子。

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.ApplicationContextAware 与 BeanNameAware

当ApplicationContext创建一个实现了org.springframework.context.ApplicationContextAware接口的对象时,这个对象实例会获得一个ApplicationContext的引用。

public interface ApplicationContextAware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

因此,beans可以以编程方式通过ApplicationContext接口操作ApplicationContext,或者是通过将引用转为一个已知的子类接口操作ApplicationContext,例如,转为ConfigurableApplicationContext。

一个常见的用法是检索使用其他beans。不过,不提倡使用这个接口,因为会提高代码与Spring框架的耦合程度,并且违反了控制反转。

Spring2.5之后,可以使用自动装配直接获得ApplicationContext,即通过constructor、byType自动装配模式。

 

当ApplicationContext创建一个实现了org.springframawork.beans.factory.BeanNameAware接口的对象时,该对象会获得一个对bean定义的名称的引用。

public interface BeanNameAware {

    void setBeanName(String name) throws BeansException;
}

 

3.其他Aware接口

ps:Aware接口的使用会导致代码与Spring API的耦合。

ps:实现Aware的bean被推荐作为用于必需的编程访问容器的基础结构beans。

 

 

Bean定义继承

一个bean定义可以包含的信息有构造器参数、属性值、初始化方法、静态工厂方法名称等。

一个孩子bean定义可以继承双亲bean定义的配置数据。

孩子定义可以重写一些值或添加一些其他的必要值。

 

如果直接通过编程方式使用ApplicationContext接口,那么孩子bean定义使用ChildBeanDefinition类表示。

当使用基于xml的配置元数据时,通过指定孩子bean的parent属性,设置双亲bean。

<bean id="parentBean" class="examples.ParentBean"></bean>
<bean id="childBean" class="examples.ChildBean" parent="parentBean"></bean>

 

孩子bean定义中如果没有指定class,那么使用双亲bean定义的class。

孩子bean类需要与双亲兼容,即必须接受双亲属性的值。

孩子bean定义继承scope,构造器参数值,属性值以及添加可选新值的双亲方法的重载。

任何指定的scope、初始化方法、destory方法或静态工厂方法设置都会重载双亲设置。

 

总是会被继承的设置有:依赖、自动装配模式、依赖检测、单例、怠惰初始化。

 

如果双亲bean定义没有指定class,那么显示指定abstract属性是必要的。

<bean id="parentBean" abstract="true"></bean>
<bean id="childBean" class="examples.ChildBean" parent="parentBean"></bean>

 

当一个定义为abstract时,一般被用作专门被子bean定义继承的纯粹模板bean定义。

如果想要使用设置了abstract的bean,那么会返回一个错误。

容器内部preInstantiateSingletons()方法将会忽视abstract的bean定义。

 

Application默认会预初始化所有的单件。

如果想让设置了class的双亲bean不被预初始化,需要将其设置为abstract。

 

 

 

翻译源:Spring官方文档

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值