spring-基础四

简介

通常情况下我们可以通过xml方式定义Bean,但是很多时候有特殊的要求我们需要自定义bean,在Spring体系下我们如何自定义Bean?

Spring 下的实体都是通过容器来管理其生命周期,实体的创建与销毁都与Spring容器管理有关,我们自定义实体的时候如何能够做到让我们的实体与Spring容器管理相关?

生命周期回调

我了将我们的实体交予Spring容器管理,Spring提供了InitializingBean 与 DisposableBean接口,容器为Spring提供了InitializingBean调用afterPropertiesSet()方法为DisposableBean调用destroy()方法,可以让实体在初始化和销毁时执行某些操作.

Bean的初始化回调
  • 实现InitializingBean 方法
    public class TestBean implements InitializingBean {
        public void afterPropertiesSet() {
        // 实体在初始化时需要做额外事情
        }
    }

  • 通过@PostConstruct注解
    public class TestBean {
        @PostConstruct
        public void init() {
        // 实体在初始化时需要做额外事情
        }
    }
  • 通过xml 配置init-method属性
    <bean id="testBean" class="com.panic.TestBean" init-method="init"/>

    public class TestBean {
        public void init() {
        // 实体在初始化时需要做额外事情
        }
    }
Bean的销毁回调
  • 实现DisposableBean 接口
    public class TestBean implements DisposableBean {
        public void destroy() {
        // 实体在销毁时需要做额外事情
        }
    }

  • 指定xml配置 destroy-method属性
    <bean id="testBean" class="com.panic.TestBean" destroy-method="cleanAll"/>

    public class TestBean {
        public void cleanAll() {
        // 实体在初始化时需要做额外事情
        }
    }
  • 通过@PreDestroy注解
    public class TestBean {
        @PreDestroy
        public void cleanAll() {
        // 实体在初始化时需要做额外事情
        }
    }
执行顺序

如果我们配置一个bean的时候及配置xml的init-method方法又继承了InitializingBean,并且还使用了@PostConstruct注解,三者同时存在的时候,执行的顺序是怎样的?
在实体初始化的时候先执行@PostConstruct所标记的方法再执行afterPropertiesSet()方法最后执行xml指定的初始化方法,所以xml指定的方法优先级是最高的.同理在实体销毁的时候执行顺序是先执行注解指定方法,再执行实现的回调destroy()方法,最后实现xml指定的销毁方法.

选择哪种形式定义生命周期回调?

Spring官网上说明:

不推荐我们使用继承方式,推荐我们使用注解的方式来完成,理由是继承的方式增加了代码与Spring框架的耦合程度.

启动与关闭回调
Lifecycle

Lifecycle接口定义任意bean说必须要要的生命周期方法,比如启动,停止与后台运行.

public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();
}

任何Spring管理的对象可能实现Lifecycle接口。然而,当ApplicationContext接收到启动和停止信号(例如:在运行时停止和重启场景),它将这些调用级联到在该上下文中定义的所有生命周期实现。通过代理到LifecycleProcessor处理,在下面清单显示:

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();
}

LifecycleProcessor 是 Lifecycle 接口的拓展,增加了两个额外的方法接收上下文的刷新与关闭.当我们实现Lifecycle接口时,如果我们想要其start或者stop执行,必须显式的调用容器的start()或者stop()方法.stop方法不一定能保证在我们之前介绍的销毁方法之前执行.

SmartLifecycle

启动和关闭顺序调用是非常重要的。如果依赖关系存在任何对象之间,依赖侧开始在被依赖之后并且它的停止在它的依赖之前。然而,在运行时,这个直接依赖是未知的。我们可能只知道某种类型的对象应该先于另一种类型的对象开始。在这种情况下,SmartLifecycle接口定义其他可选,即getPhase()方法在它的父接口Phased被定义。
Phased接口:

public interface Phased {

    int getPhase();
}

SmartLifecycle接口:

public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();

    void stop(Runnable callback);
}

启动时,最初被依赖的对象首先启动。停止时,顺序相反。因此,实现SmartLifecycle接口并且getPhase()方法返回Integer.MIN_VALUE的对象将在第一个启动和最后一个停止。另一种类型,Integer.MAX_VALUE阶段值指示这个对象最后一个被启动并且第一个被销毁 (可能因为它依赖其他处理运行)。当考虑这个阶段值的时候,重要的是要知道,任何未实现SmartLifecycle的“正常”生命周期对象的默认阶段为0。因此,任何负的阶段值表示对象应该启动在这些标准的组件之前(停止在它们之后)。对于任何正阶段值,情况正好相反。

停止方法通过SmartLifecycle接受一个回调被定义。任何实现必须在实现的关闭处理完成后调用回调的run()方法。这将在必要时启用异步关闭,因为LifecycleProcessor接口的默认实现DefaultLifecycleProcessor会等待其超时值,以等待每个阶段内的对象组调用该回调。每个阶段默认超时时间30秒。我们可以通过定义一个bean 命名为lifecycleProcessor在上下文中覆盖这个默认生命周期处理实例。如果我们想仅仅修改超时时间,定义以下内容就足够了:

<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
    <!-- 设置超时时间 -->
    <property name="timeoutPerShutdownPhase" value="10000"/>
</bean>

像前面提到的,LifecycleProcessor接口定义回调方法为上下文更好的刷新和关闭。后者驱动关闭过程,就好像已经显式调用了stop()一样,但是它在上下文关闭时发生。另一方面,“refresh”回调启用了SmartLifecycle bean的另一个特性。当这个上下文被刷新(所有的bean被初始化和实例化),即回调被调用。在那个阶段,默认的生命周期处理器通过每个SmartLifecycle对象的isAutoStartup()方法检测返回boolean值。如果true,对象在那个阶段被启动而不是等待上下文或者自身的start()方法显示调用(与上下文刷新不同,对于标准上下文实现,上下文启动不会自动发生)。

ApplicationContextAware和BeanNameAware

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

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

因此,这些bean可以编程地操作ApplicationContext创建它们,通过ApplicationContext接口或者转换引用为已知的这个接口的子类(例如:ConfigurableApplicationContext,它暴露了附加的功能)。一种用途是通过编程方式检索其他bean。有时候这个能力非常有用。然而,一般的,我们应该避免使用,因为它耦合Spring的代码并且不遵循控制反转格式,将协调者作为bean的属性。ApplicationContext其他方法提供获取资源文件、发布应用事件和访问MessageSource。这些额外的特性在ApplicationContext的附加能力中描述。

自动装配是获得对ApplicationContext的引用的另一种选择。传统的constructor和byType自动装配模式(在自动装配协作器中描述)能够为构造函数或者Setter方法参数提供一个ApplicationContext类型的依赖。为了获得更大的灵活性,包括自动装配字段和多个参数方法的能力,可以使用基于注解的自动装配特性。如果这样做,ApplicationContext自动装配到字段、构造参数或方法参数,如果字段、构造函数或方法带有@Autowired注解那么该参数期望ApplicationContext类型。

其他Aware接口

所有的Aware接口都是为了能让我们拿到容器中相关资源,比如BeanNameAware,可以让我们拿到Bean的名称,ApplicationContextAware 可以让我们拿到整个容器.
除了ApplicationContextAware和BeanNameAware之外,Spring提供了广泛的Aware回调接口,这些接口使Bean向容器指示它们需要一些基础结构依赖性。作为基本规则,这个名字指示依赖类型。下面总结最重要的Aware接口:

Spring 不推荐我们使用这些接口,原因是使用了这些接口会使我们代码与Spring容器发生了强耦合,违背IOC原则.

参考文档

Spring官网

Spring 中Bean的生命周期

posted @ 2023-02-03 14:42  年年糕  阅读(15)  评论(0编辑  收藏  举报