美文网首页
Spring IoC容器之自定义bean的生命周期及定义继承

Spring IoC容器之自定义bean的生命周期及定义继承

作者: 夏与清风 | 来源:发表于2019-07-15 19:55 被阅读0次

一、自定义bean的生命周期

通过实现spring的InitializingBean和DisposableBean接口,可以让容器来管理bean的生命周期。容器在调用afterPropertiesSet()方法后和调用destroy()方法前会允许bean在初始化和销毁bean时执行以下操作,但使用这些接口就与springAPI产生了耦合。

解耦合的处理方式有两种:

\bullet 使用JSR-250的@PostConstruct和@PreDestroy注解是spring应用生命周期回调的最佳实践。

\bullet 使用init-method和destroy-method的定义来解耦spring接口。

spring框架使用BeanPostProcessor接口的实现来处理接口的回调,BeanPostProcessor能找到并调用合适的方法。如果需要定制spring不直接提供的生命周期行为,可自行实现一个BeanPostProcessor。

除初始化回调和销毁回调外,spring管理的对象也实现了Lifecycle接口,让管理的对象在容器的生命周期内启动或关闭。

1、初始化回调

org.springframework.beans.factory.InitializingBean接口允许bean在所有必要依赖配置完成后执行初始化bean。

接口定义

不建议使用InitializingBean接口,否则会将代码耦合到spring特定接口上。使用@PostConstruct注解或指定一个POJO的实现方法的方式更好。在基于XML的配置元数据时可以使用init-method属性来指定一个没有参数的方法。使用Java配置时也可以使用@Bean中的init-method属性初始化回调。示例如下:

<bean id="initBean" class="xx.InitBean" init-method="init" />

public class InitBean {

    public void init(){...}

}

以下方式等效于以上方式,但会耦合到spring中:

<bean id="initBean" class="xx.InitBean" />

public class InitBean implements InitializingBean {

    public void afterPropertiesSet(){...}

}

2、销毁回调

实现org.springframework.beans.factory.DisposableBean接口就可以让容器通过回调来销毁bean锁引用的资源。

接口定义

与InitializingBean接口类似,同样不推荐使用。使用@PreDestroy注解或指定一个bean支持的配置方法或在基于XML的配置元数据中,在bean标签上指定destroy-method属性。在基于Java的配置中可以配置@Bean中的destroy-method属性实现销毁回调。示例如下:

<bean id="desBean" class="xx.DesBean" destroy-method="cleanup" />

public class DesBean {

    public void cleanup(){...}

}

以下方式等效于以上方式,但会耦合到spring中:

<bean id="desBean" class="xx.DesBean" />

public class DesBean implements DisposableBean {

    public void destroy(){...}

}

3、结合生命周期机制

spring2.5之后,有3种方式来控制bean的生命周期行为:

\bullet InitializingBean和DisposableBean回调接口

\bullet 自定义的init()和destroy()方法

\bullet 使用@PostConstruct和@PreDestroy注解

如果一个bean配置了多个生命周期机制,并且含有不同的方法名,执行顺序如下:

初始化时:

1)包含@PostConstruct注解的方法

2)在InitializingBean接口中的afterPropertiesSet()方法

3)自定义的init()方法

销毁时:

1)包含@PreDestroy注解的方法

2)在DisposableBean接口中的destroy()方法

3)自定义的destroy()方法

4、启动和关闭回调

Lifecycle接口中为任何有生命周期需求的对象定义了一些基本方法,spring管理的任何对象都可以实现其接口。当ApplicationContext接收到了启动或停止信号时,它会通知所有上下文包含的生命周期对象,通过LifecycleProcessor接口来串联上下文中的Lifecycle来实现对象。

Lifecycle接口定义 LifecycleProcessor接口定义

LifecycleProcessor是对Lifecycle的扩展,它增加了2个方法来对上下文的刷新和关闭作出反应。

如果不同bean之间存在depends-on的关系,被依赖的一方需要更早的启动或关闭,有时直接的依赖是未知的,开发者可能并不值得哪些类型需要更早的进行初始化。SmartLifecycle接口定义了另外一种选项,即其父接口Phased中的getPhase()方法。

SmartLifecycle接口定义   Phased接口定义

当启动时,拥有最低phased的对象优先启动,当关闭时,按相反的顺序。如果一个对象实现了SmartLifecycle接口,其getPhase()返回了Integer.MIN_VALUE,就会让该对象最早启动,最晚销毁。如果getPhase()返回了Integer.MAX_VALUE,就会让该对象最晚启动,最早销毁。当使用phased的值时,需要知道正常没有实现SmartLifecycle的Lifecycle对象的默认值,此值为0。任何负值都会将标明对象在标准组件启动前启动,在标准组件销毁后销毁。

SmartLifecycle定义了一个stop()回调函数,任何实现了其接口的方法都必须在关闭流程完成后调用回调中的run()方法,这样可以使其异步关闭。LifecycleProcessor的默认实现DefaultLifecycleProcessor会等到配置的时间超时后再调用回调,每阶段的超时默认30S。可以通过定义一个名为lifecycleProcessor的bean来覆盖默认的生命周期处理器,若需要配置超时时间,可如下配置:

<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">

    <!-- 超时时间,单位毫秒 -->

    <property name="timeoutPerShutdownPhase" value="10000" />

</bean>

SmartLifecycle接口定义回调方法来刷新和关闭上下文,如果关闭则说明stop()已被调用,就会驱动关闭流程;如果上下文正在关闭,就不会发生这种情况。刷新的回调会使用SmartLifecycle的另一特性:当上下文刷新完毕就会调用回调,默认的生命周期处理器会检查每一个SmartLifecycle对象的isAutoStartup()返回值。若为true,对象将会自动启动而非等待明确的上下文调用,或调用自己的start()。phased的值及depends-on会决定对象启动和销毁的顺序。

5、在非web应用中关闭Spring IoC容器

在桌面客户端等非web环境下使用Spring IoC容器,需要在JVM上注册一个关闭的钩子,确保在关闭IoC容器时能够调用相关的销毁方法来释放引用的资源,同时也需要正确的配置和实现销毁回调。

在ConfigurableApplicationContext接口调用registerShutdownHook()方法来注册销毁的钩子。示例如下:

ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");

...

ctx.registerShutdownHook();

6、ApplicationContextAware和BeanNameAware

当ApplicationContext在创建实现了org.springframework.context.ApplicationContextAware的对象时,该对象的实例会包含一个到ApplicationContext的引用。这样bean可以通过编程的方式操作和创建ApplicationContext。

ApplicationContextAware接口定义

通过ApplicationContext接口或通过将引用转换为已知接口的子类,如ConfigurableApplicationContext,其中一个用法是可以通过编程的方式来获取其他bean。但不建议这样做,这样代码会耦合到spring。ApplicationContext的其他方法可以提供资源访问、发布应用事件、进入MessageSource等功能。

自动装载也是获得ApplicationContext的一种方式,构造函数和通用类型的装载方式,是指可以通过构造函数或setter方法注入,也可以通过注解注入的方式。

当ApplicationContext创建一个实现了org.springframework.beans.factory.BeanNameAware接口的类,这个类就可以针对其名称进行配置。这个回调的调用发生在属性配置完成后,初始化回调之前,如InitializingBean.afterPropertiesSet()及自定义的初始化方法等。

BeanNameAware定义

7、其他Aware接口

spring还提供了其他Aware接口来让bean通知容器,这些bean需要一些具体的注入的依赖信息,如下:

\bullet ApplicationContextAware:声明的ApplicationContext

\bullet ApplicationEventPublisherAware:ApplicationContext中的事件发布器

\bullet BeanClassLoaderAware:加载bean使用的类加载器

\bullet BeanFactoryAware:声明的BeanFactory

\bullet BeanNameAware:bean的名称

\bullet LoadTimeWeaverAware:加载期间处理类定义的Weaver

\bullet MessageSourceAware:解析消息的配置策略

\bullet NotificationPublisherAware:spring JMX通知发布器

\bullet ResourceLoaderAware:配置的资源加载器

上面这些接口的不建议使用,因为其会违反IOC原则。

二、定义继承

bean的定义可以包含构造方法参数、属性值、容器特定的信息等。子bean定义可以从父bean定义的配置元数据来继承,它可以覆盖或添加一些所需的值。父子bean是一种典型的模板形式。

如果编程式的使用ApplicationContext接口,子bean的定义可以通过ChildBeanDefinition类来表示,但使用更多的是在类似于ClassPathXmlApplicationContext中声明式的配置bean的定义。如果使用基于XML的配置时,可以在子bean中使用parent属性,用于标识父bean。

<bean id="testBean" abstract="true" class="org.springframework.beans.TestBean">

  <property name="name" value="parent"/>

  <property name="age" value="10"/>

</bean>

<bean id="testBean1" class="org.springframework.beans.TestBean1" parent="testBean" init-method="initialize">

  <property name="name" value="override"/>

</bean>

子bean如果没有指定class,它将使用父bean定义的class或进行重载。在重载情况下子bean必须与父bean兼容,即它必须接受父bean的属性值。

子bean定义可以从父bean继承作用域、构造器参数、属性值和可重写的方法,还可以增加新值。开发者指定的任何作用域、初始化方法、销毁方法、静态工厂方法设置,都会覆盖相应的父bean设置。其余的设置总是取自子bean的定义,如depends-on、autowire mode、dependency check、singleton、scope和lazy init等。

上例中使用abstract属性明确表明父bean是抽象的,如果父bean定义没有明确指出所属类,则需要标记父bean定义为abstract。

<bean id="testBean" abstract="true">

  <property name="name" value="parent"/>

  <property name="age" value="10"/>

</bean>

<bean id="testBean1" class="org.springframework.beans.TestBean1" parent="testBean" init-method="initialize">

  <property name="name" value="override"/>

</bean>

上例中只有一个bean定义为abstract,它只能作为一个纯粹的为子bean定义的bean模板。独立使用这样一个abstract的父bean,把它作为另一个bean的引用,或根据这个父bean的id显式调用getBean(),都将返回错误。

ApplicationContext默认会预实例化所有单例bean,如果把一个bean定义仅作为模板来用,同时给它指定了class属性,就必须设置abstract=true,否则ApplicationContext就会预实例化这个abstract bean。

--参考文献《Srping5开发大全》

相关文章

网友评论

      本文标题:Spring IoC容器之自定义bean的生命周期及定义继承

      本文链接:https://www.haomeiwen.com/subject/ccekkctx.html