IoC容器6——自定义bean属性

自定义bean属性

1 生命周期回掉函数

为了与容器管理bean的生命周期相互做用,能够实现Spring的InitializingBean和DisposableBean接口。容器经过调用前者的afterPropertiesSet()函数和后者的destroy()函数,容许bean在初始化和销毁时执行某些操做。java

JSR-250的@PostConstruct和@PreDestory注解通常被认为在现代Spring应用中是接受生命周期回掉的最佳实践。使用这些注解意味着不须要与Spring的特定接口耦合。web

若是不想使用JSR-250,可是仍想消除耦合,可使用init-method和destroy-method对象定义元数据。spring

在内部,Spring Framework使用BeanPostProcessor的实现来处理它能够找到的任何回调接口,并调用适当的方法。若是须要自定义的特征或其它生命周期行为,Spring没有提供开箱即用的实现,能够本身实现BeanPostProcessor。编程

做为实例化和销毁回调函数的补充,Spring管理的对象也能够实现Lifecycle接口,以便这些对象能够加入容器本身的生命周期驱动的启动和关闭进程。app

初始化回调函数

实现org.springframework.beans.factory.InitializingBean接口容许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
    }
}

上例等价于下面的例子,可是代码并不与Spring耦合。this

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
    public void afterPropertiesSet(){
        // do some initialization work
    }
}

销毁回调函数

实现org.springframework.beans.factory.DisposableBean接口容许容器一个bean在被容器销毁时得到回调。DisposableBean接口定义了一个方法:代理

void destroy() throws Exception;

并不推荐使用DisposableBean回调接口,由于没有必要将代码与Spring耦合。做为替代,可使用@PreDestory注解或者指定一个bean定义支持的范型方法。使用XML格式的配置元数据,可使用<bean/>元素的destroy-method属性。使用Java配置,可使用@Bean的destroyMethdo属性。code

<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {
    public void cleanup(){
        // do some destruction work (like releasing pooled connections)
    }
}

上例等价于下面的例子,可是代码不与Spring耦合。

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
    public void destroy(){
        //do some destruction work (like releasing pooled connections)
    }
}

<bean>元素的destroy-method属性能够指定一个特殊的值——inferred,来指示Spring自动发现指定bean类的公有的close或者shutdown方法(所以任何实现java.lang.AutoCloseable或者java.io.Closeable的类都将匹配)。这个特殊的值(inferred)也能够设置为<beans>元素的default-destroy-method属性以便将此行为应用整个bean的集合。注意,这是Java配置的默认行为。

默认的初始化和销毁方法

当你不使用Spring指定的InitializingBean和DisposableBean回调接口来编写初始化和销毁回调函数,通常的能够将方法命名为init(), initialize(), dispose()等等。理想状态下,这些生命周期回调函数的名字在一个工程中是标准化的,所以全部开发者使用相同的方法名字并保证一致性。

能够配置Spring容器查找每一个bean的初始化和销毁回调函数名。这意味着应用开发者能够编写应用类而且使用名为init()的初始化回调函数,而不用在每一个bean定义配置init-method="init"属性。这个特性也保证了初始化和销毁回调函数的命名一致性。

假设初始化方法命名为init()、销毁方法命名为destory(),那么Class会相似于下面的例子

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属性来覆盖默认的属性。

Spring容器保证被配置的初始化回调函数会在bean的依赖关系装配完成后当即被调用。所以是在生的bean引用上调用初始化回调函数,这意味着AOP拦截器等等还没有做用于bean。一个目标bean首先被完整建立,而后应用一个具备拦截器链的AOP代理。若是目标bean和代理被分别定义,代码甚至能够绕过代理与原始目标bean进行交互。所以将拦截器应用于初始化方法是不一致的,由于这么作会将目标bean的生命周期和代理/拦截器耦合在一块儿同时当代码直接于目标bean进行交互时会产生奇怪的语义。

联合使用生命周期机制

在Spring 2.5中有三种控制bean生命周期的行为:InitializingBean和DisposableBean的回调接口;用户自定义的init()和destroy()方法;和@PostConstruct、@PreDestroy注解。能够联合使用这三种机制来控制个bean。

若是一个bean被配置了多个生命周期机制,而且每一个机制配置了不一样的方法名,那么每一个配置的方法都会按下述的顺序执行。然而,若是多种机制配置了相同的方法名——例如,init()初始化方法—,那么方法会被执行一次。

同一个bean配置了多种生命周期机制,不一样名称的方法会按下面的顺序执行:

  • 标记了@PostConstruct注解的方法;
  • IntializingBean接口中定义的afterPropertiesSet()方法;
  • 用户配置的init()方法。

销毁方法按以下顺序调用:

  • 标记了@PreDestroy注解的方法;
  • DisposableBean接口定义的destroy()方法;
  • 用户配置的destroy()方法。

启动和关闭回调函数

Lifecycle接口为任何具备本身生命周期要求的对象定义必要的方法(例如开始和中止一些后台进程):

public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();

}

任何Spring管理的对象能够实现这个接口。而后,当ApplicationContext自身接收到开始和中止信号,例如在运行时的中止/重启场景,它会级联的调用定义在其中的所用Lifecycle实现。它经过委托给LifecycleProcessor来完成这项工做:

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();

}

请注意,LifeCycleProcessor自身就是Lifecycle接口的扩展。它也添加了两个接口来响应上下文被刷新和关闭的状况。

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

启动和关闭调用的顺序很重要。若是两个对象之间有依赖关系,依赖的一方将会比它的依赖关系晚启动,而且早关闭。然而,许多时候直接的依赖关系是未知的。您也许只知道一些特定类型的对象要比另外一些类型的对象优先启动。在这种状况下,SmartLifecycle接口定义了另外一种选择,在它的父接口Phased中定义的getPhase()方法。

public interface Phased {

    int getPhase();

}
public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();

    void stop(Runnable callback);

}

在启动时,具备低phase的对象首先启动,而且在关闭时顺序相反。所以,实现了SmartLifecycle接口而且getPhase()方法的返回值是Integer.MIN_VALUE的对象将对一个被启动并最后一个被结束。在另外一端,phase的值是Integer.MAX_VALUE代表对象将被最晚启动、最先结束(有可能它须要其它的处理来运行)。任何普通的没有实现SmartLifecycle接口的Lifecycle对象的phase值为0。所以任何负的phase值代表对象早于普通组件启动,晚于它们关闭。任何正相位值反之亦然。

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()方法返回的布尔值。若是为真,则该对象将在此时启动,而不是等待显式调用上下文或其本身的start()方法(与上下文刷新不一样,对于标准上下文实现,上下文启动不会自动发生)。phase值以及任何依赖关系将以与上述相同的方式肯定启动顺序。

在非web应用中优雅的关闭Spring IoC容器

这部分仅应用于非web应用。Spring基于web的ApplicationContext实现已经包含在相关的web应用关闭时优雅的关闭Spring IoC容器的相关代码。

若是在非web应用环境中使用Spring IoC容器;例如在一个桌面客户端环境;能够注册一个JVM关闭回调。这么作保证了优雅的关闭而且会调用singleton bean的相关销毁方法,以保证全部资源会被释放。固然,必须正确的配置和实现这些销毁方法。

为了注册关闭回调,须要调用在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...

    }
}

2 ApplicationContextAware 和 BeanNameAware

当一个ApplicationContext建立一个实现了org.springframework.context.ApplicationContextAware接口的对象实例,这个实例被提供一个ApplicationContext的引用。

public interface ApplicationContextAware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

所以这个bean能够经过编程的方法操做建立它的ApplicationContext,经过ApplicationContext接口或者将其引用转换成暴露额外功能的已知的接口子类,例如ConfigurableApplicationContext。一个做用时经过编程的方法获取其它bean。有时候这种能力颇有用;可是,通常状况下你须要避免它,由于它将代码和Spring耦合在一块儿而且没有遵循控制反转的风格——协做者做为属性提供给bean。ApplicationContext的其它方法提供获取文件资源、发布应用事件和获取MessageSource的能力。

在Spring 2.5中,自动装配是另外一个获取ApplicationContext引用的选择。传统的构造函数和根据类型的自动装配模式能够为构造函数参数或setter方法参数提供ApplicationContext类型的依赖。为了更多的灵活性,包括自动装配字段的能力和多种参数方法,可使用你的基于注解的自动装配功能。若是这样作,ApplicationContext将自动装配那些标注了@Autowired注解的类型为ApplicationtContext的字段、构造函数参数或者方法参数。

当ApplicationContext建立了一个类实现了org.springframework.beans.factory.BeanNameAware接口,该类被提供其定义的名称的引用。

public interface BeanNameAware {

    void setBeanName(String name) throws BeansException;

}

这个方法的调用发生在普通属性设置以后在初始化回调(例如InitializingBean的afterPropertiesSet方法或者用户自定义的初始化方法)调用以前。

3 其它感知接口

除了上文所述的ApplicationContextAware和BeanNameAware,Spring还提供了一批感知接口容许bean向容器指明它们须要的明确基础设施依赖。最重要的感知接口整理以下,做为通常规则,名称是依赖关系类型的良好指示:

名字 注入的依赖
ApplicationContextAware 声明的ApplicationContext
ApplicationEventPlulisherAware ApplicationContext中的事件发布器
BeanClassLoaderAware 加载Bean使用的类加载器
BeanFactoryAware 声明的BeanFactory
BeanNameAware Bean的名字
BootstrapContextAware 容器运行的资源适配器BootstrapContext,一般仅在JCA环境下有效
LoadTimeWeaverAware 加载期间处理类定义的weaver
MessageSourceAware 解析消息的配置策略
NotificationPublisherAware Spring JMX通知发布器
PortletConfigAware 容器当前运行的PortletConfig,仅在web下的Spring ApplicationContext中可见
PortletContextAware 容器当前运行的PortletContext,仅在web下的Spring ApplicationContext中可见
ResourceLoaderAware 配置的资源加载器
ServletConfigAware 容器当前运行的ServletConfig,仅在web下的Spring ApplicationContext中可见
ServletContextAware 容器当前运行的ServletContext,仅在web下的Spring ApplicationContext中可见

再次注意,使用这些接口将代码和Spring API绑定,而且不遵循控制反转样式。所以,对于基础设施bean建议使用编程的访问方式。

相关文章
相关标签/搜索