深入理解Spring4框架(六)——自定义Bean属性

来源:互联网 发布:中国菜刀源码 编辑:程序博客网 时间:2024/04/27 07:11


    开发人员可以在Spring Bean的生命周期过程中,通过初始化回调方法、销毁回调方法来完成一些必要的功能。通过实现ApplicationContext相关的接口,也可以程序化的方式来操作与控制创建对象的ApplicationContext。

1 生命周期回调

    容器对Bean生命周期进行管理,为了便于与之交互,可以实现InitializingBean和DisposableBean接口,容器会在Bean初始化之前调用前者的afterPropertiesSet(),销毁之前调用后者的destroy。

    JSR-250的@PostConstruct和@PreDestroy注解被认为是现代Spring应用处理生命周期回调的最佳实践,使用这些注解,可以避免代码和Spring的特定接口耦合。

如果不想使用JSR-250注解,但又想与Spring框架解耦,也可以考虑使用init-method和destroy-method对象定义元数据。

    在Spring中,Spring框架使用BeanPostProcessor实现来执行回调接口,然后调用合适的方法。如果想使用Spring所提供之外的自定义功能,可以自己实现BeanPostProcessor。

    除了初始化和销毁回调之外,Spring所管理的对象也可以实现Lifecycle接口,这些对象就可以参与到容器自身生命周期所驱动的启动和关闭流程。

1.1 初始化回调

    在一个Bean的所有必要属性都被容器设值之后,org.springframework.beans.factory.InitializingBean接口允许执行一些初始化工作。InitializingBean接口只指定了一个方法:

void afterPropertiesSet() throws Exception;

    一般不推荐使用InitializingBean接口,因为它使得代码和Spring耦合了。推荐使用@PostConstruct注解,或者指定一个POJO初始化方法。在基于XML配置元数据的例子中,使用init-method属性来指定无参方法的名字。

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>public class ExampleBean {    public void init() {        //做一些初始化工作    }}

和下面的一样

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

public class AnotherExampleBean implements InitializingBean {    public void afterPropertiesSet() {        //做一些初始化工作    }}

1.2 销毁回调

    实现了org.springframework.beans.factory.DisposableBean接口的Bean,在包含它的容器被销毁的时候,会做一次回调。DisposableBean指定了一个接口:

void destroy() throws Exception;

    不建议使用DisposableBean回调接口,因为它的代码与Spring进行了不必要的耦合。建议使用@PreDestroy注解或Bean定义中支持的泛型方法。在基于XML的配置元数据中,在<bean/>中使用destroy-method属性。在Java配置中,可以使用Bean的destroyMethod属性。例如:

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

public class ExampleBean {    public void cleanup() {        //做一些销毁任务(比如释放连接池的连接)    }}

以上的配置等同于:

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

public class AnotherExampleBean implements DisposableBean {    public void destroy() {        //做一些销毁任务(比如释放连接池的连接)    }}

1.3 默认初始化和销毁方法

    当不使用Spring指定的InitializingBean和DispposableBean回调接口时,通常都是自己编写的如nit(),initialize(), dispose()等方法。理想情况下,这种生命周期回调方法的命名是标准的,贯穿于整个工程,使得所有的开发者都使用相同的方法名,确保一致性。

    可以配置Spring容器,让它查找每个Bean的初始化和销毁回调方法的名称。这就意味着,开发者可以在编写好类之后,直接使用一个称为init()的初始化回调,而不需为每个Bean配置init-method=”init”属性。当Bean被创建的时候,Spring的IoC容器就会调用init()。

    假如初始化回调方法为init(),销毁回调方法为destroy,那么类的组织如下所示:

public class DefaultBlogService implements BlogService {    private BlogDao blogDao;    public void setBlogDao(BlogDao blogDao) {        this.blogDao = blogDao;    }    // 初始化回调方法    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>

    default-init-method属性使得Spring IoC容器将init识别为初始化回调方法。

    初始化回调是在原生Bean引用上调用的,也就是说,这个时候AOP拦截器之类的东西还没有应用到Bean上面。比如,只有一个目标Bean被完全创建之后,一个AOP代理才会被应用到这个Bean上。因此,如果将拦截器应用到初始化方法中,将会出现不一致的问题,因为这样做会将目标Bean的生命周期与代理藕合起来。

1.4生命周期机制的组合应用

    在Spring2.5中,可以通过三种方式来控制Bean的生命周期:InitializingBean和DisposableBean回调接口;自定义init()和destroy()方法;还有@PostConstruct和@PreDestroy注解。可以组合这些机制来控制给定的Bean。

    如果为一个Bean配置了多个生命周期机制,那么每个配置的方法将按以下顺序执行:

(1)添加了@PostConstruct注解的方法。

(2)InitializingBean回调接口所定义的afterPropertiesSet()方法。

(3)自定义的init()方法。

    销毁方法调用顺序也类似:

(1)添加了@PreDestroy注解的方法。

(2)DisposableBean回调接口所定义的destroy()方法。

(3)自定义的destroy()方法

    然而,如果在不同的生命周期机制中配置了相同的方法(比如使用init()来作为初始化方法),超过1一个生命周期机制,那么init()只会被执行一次。

1.5 启动和停止回调

    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的一个扩展,它也为上下文被刷新和关闭添加了两个回应方法。

    启动和关闭调用的顺序也很重要。如果两个对象存在依赖关系,依赖方将会晚于被依赖方启动,依赖方会早于被依赖方关闭。然而,有时直接引用是未知的,也许只知道确定类型的对象应该先于另一种类型的对象启动。在那些场景中,SmartLifecycle接口定义了另一个选项,也就是超类接口定义的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值的时候,需要知道,那些没有实现SmartLifecycle接口的Lifecycle对象phase默认值为0.

    可见,SmartLifecycle定义的stop方法接受一个回调。这个接口的任何实现的关闭进程完成之后,都需要调用回调参数的run()方法。这就使得在必要的时候可以异步关闭,而LifecycleProcessor和DefaultLifecycleProcessor的默认实现将会等待一系列对象来调用那个回调参数,直到超时。默认的每个phase的超时时间是30秒。可以在上下文中重新定义一个名为”lifecycleProcessor”的Bean来覆盖默认的生命周期处理器。如果仅仅想修改超时时间,如下定义就足够了:

<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">    <!-- 超时值,毫秒 -->    <property name="timeoutPerShutdownPhase" value="10000"/></bean>

    LifecycleProcessor接口也为context的刷新和关闭定义了回调方法。若stop()被显式调用了,后者就仅会驱动关闭进程,但是在上下文关闭的时候也会有这样的行为。而refresh回调就会激活SmartLifecycle的另一个功能,当上下文被刷新后(所有的对象均被实例化和初始化之后),那个回调就会被调用。这时,默认的生命周期处理器将会检查每个SmartLifecycle对象的isAutoStartup()方法返回的boolean值。如果为true的话,然后那个对象将会立即启动,而不是等待一个显式的调用。

2 ApplicationContextAware和BeanNameAware

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

public interface ApplicationContextAware {    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;}

    通过ApplicationContext接口,Bean可以从程序上操作创建它的ApplicationContext。也可以将引用转换为它的子类,比如ConfigurableApplicationContext,它暴露了更多的功能。一种用法就是以程序的方式检索其它Bean。有时,这个功能很有用;然而,一般情况下应该避免使用它,因为它与Spring代码耦合了,并且它没有遵守控制反转风格。传统的constructor和byType自动注入模型可以为构造器参数或者setter方法参数提供一种ApplicationContext类型依赖。为了使得程序更灵活,请使用新的基于注解的功能@Autowired,更多详细信息请关注后续讲解。

    当ApplicationContext创建了一个实现org.springframework.beans.factory.BeanNameAware接口的类时,这个类就拥有了这个对象名称的引用。

public interface BeanNameAware {    void setBeanName(string name) throws BeansException;}
0 0
原创粉丝点击