Spring自定义Bean的特性


如何定制Spring Bean

Spring提供了一系列用于自定义Bean特性的接口,主要有以下3类:

生命周期回掉

为了能够与Spring管理的bean的生命周期交互,可以实现InitializingBeanDisposableBean接口,Spring容器会在初始化和销毁bean的时期分别回掉afterPropertiesSet()destroy()方法

tips:Spring官方还是推荐使用JSR-250规范的 @PostConstruct@PreDestroy 注解进行生命周期回掉声明,因为这样不会耦合Spring的代码,当然如果既不想耦合Spring的代码又想进行生命周期回掉,可以给beanDefinition指定init-methoddestroy-method属性

在Spring内部,Spring使用BeanPostProcessor来完成生命周期的各种回掉函数,如果想要自定义或者实现Spring没有提供的生命周期行为,可以考虑实现BeanPostProcessor来完成目标

除了生命周期回掉函数之外,Spring管理的对象也可能实现了Lifecycle接口,所以这些对象能够参与应用的启动与停止的过程。

初始化回掉

org.springframework.beans.factory.InitializingBean 接口能够在容器为Bean设置了所有必要的属性后执行初始化工作。

public interface InitializingBean {

    void afterPropertiesSet() throws Exception;

}

但是,秉承不耦合Spring的宗旨,建议使用 @PostConstruct注解或指定生命周期回掉函数。比如通过@Bean注解:

public class BeanOne {

    public void init() {
        // initialization logic
    }
}
@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public BeanOne beanOne() {
        return new BeanOne();
    }

    @Bean(destroyMethod = "cleanup")
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

@PostConstruct注解实现:

public class CachingMovieLister {

    @PostConstruct
    public void populateMovieCache() {
        // populates the movie cache upon initialization...
    }

    @PreDestroy
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
    }
}

销毁回掉

实现org.springframework.beans.factory.DisposableBean接口的bean能够在bean销毁的时候被回掉:

public class CachingMovieLister {

    @PreDestroy
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
    }
}

同上推荐的方式是:

public class BeanTwo {

    public void cleanup() {
        // destruction logic
    }
}

@Configuration
public class AppConfig {

    @Bean(destroyMethod = "cleanup")
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

默认的初始化和销毁的回掉方法

当不使用InitializingBeanDisposableBean接口实现生命周期回掉时,可以使用init(), initialize(), dispose()等方法名定义回掉方法,通常来说这是标准的生命周期回掉方法名,并且所有的开发者都会使用这些方法名来保持统一性。

当使用init()来定义生命周期回掉时,不需要指定bean定义的init-method="init"属性。

所以初始化回掉就默认使用init(),销毁回掉使用dispose()

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

同时,可以通过init-methoddestroy-method来覆盖默认的生命周期回掉

Spring容器能够保证一旦bean的所有依赖被注入就会立马回掉声明周期函方法,所以初始化回掉是发生在bean还没有被Spring Aop代理之前的时期。首先目标bean被完全创建,其次Aop再进行拦截。

组合生命周期回掉机制

Spring 2.5之后我们有3种选择来介入bean的生命周期:

并且我们可以组合使用这3种方式来介入bean的生命周期

注意:如果一个bean的生命周期被配置了多个回掉方法,那么这些回掉方法不能同名,并且将会按下面描述的顺序执行

Bean初始化回掉顺序:

  1. @PostConstruct注解标注的方法最先执行
  2. InitializingBeanafterPropertiesSet() 方法
  3. 自定义的 init()方法

bean销毁回掉顺序同上:

  1. @PreDestroy注解标注的方法最先执行
  2. DisposableBeandestroy() 方法
  3. 自定义的destroy()方法

启动和停止回掉

Spring的Lifecycle接口为任何一个有自己的生命周期的bean定义了必要的方法,比如:正在启动和停止一些后任务

public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();
}

任何Spring管理的对象都可以实现Lifecycle接口,ApplicationContext在接收到启动和停止信号时,会将其传递给所有实现Lifecycle接口的对象,其内部时通过LifecycleProcessor来实现:

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();
}

LifecycleProcessor本身也继承了Lifecycle接口,并且添加了响应容器刷新和关闭的方法。

注意:org.springframework.context.Lifecycle接口只是定义了启动的和停止的规范,但是并不能在上下文环境刷新时自动启动,如果需要实现自动启动,考虑实现org.springframework.context.SmartLifecycle接口

更重要的是,stop()方法的回掉不能保证在bean的销毁回掉之前被调用,在正常的停止过程中,实现Lifecycle的bean会首先收到容器停止的回掉其次是bean销毁的回掉,在容器热刷新等情况下,只会接收到bean销毁的回掉。

bean之间的启动和停止的调用顺序非常重要,如果2个bean之间有使用@dependsOn注解标识依赖关系,则依赖方会在被依赖方之后启动,在被依赖方之前停止,通常来说,bean之间的直接依赖关系是未知的,在这种情况下,SmartLifecycle接口定义了另一种方式来解决:其继承的父接口PhasedgetPhase()方法。

public interface Phased {

    int getPhase();
}

SmartLifecycle接口:

public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();

    void stop(Runnable callback);
}

当应用启动时,phase(阶段)值越低启动优先级越高,因此,如果一个实现了SmartLifecycle接口的对象的getPhase()方法返回值为Integer.MIN_VALUE将会成为最先启动的,最后停止的对象,反之亦然,没有实现SmartLifecycle的普通Lifecycle接口的phase值是0,因此任何负数phase值都将会优先于这些普通的对象创建。

SmartLifecycle定义的停止方法接收一个回掉函数,任何实现SmartLifecycle的对象都必须在应用停止的时候调用回掉函数的run()方法,DefaultLifecycleProcessor支持异步回掉停止方法,并且每个阶段的bean回掉超时默认是30s,可以通过自定义DefaultLifecycleProcessor进行修改:

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

LifecycleProcessor接口定义了在容器刷新和关闭时候的回掉方法,当所有的bean都实例化和初始化完后(即容器刷新)LifecycleProcessor会调用SmartLifecyclebean的isAutoStartup()方法,如果返回true,则SmartLifecyclebean的start()方法会被直接回掉,而不是等待显式的调用。

非web环境优雅关闭Spring ioC容器

如果在非web环境中使用Spring ioC容器,需要注册一个钩子到JVM上,这样能够确保调用所有单例bean的销毁方法使得所有资源被释放,所以必须正确的配置和实现这些销毁回掉函数。

调用ConfigurableApplicationContextregisterShutdownHook()方法注册钩子:

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

文章作者: Ubi-potato
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Ubi-potato !
评论
  目录