Spring-IOC bean 生命周期之 Lifecycle 钩子

Lifecycle callbacks
Initialization callbacks、Destruction callbacks
要与容器的bean生命周期管理交互,即容器在启动后和容器在销毁前对每个bean执行操作,有如下三种方法:
1.实现Spring框架的InitializingBean和DisposableBean接口。容器为前者调用afterPropertiesSet()方法,为后者调用destroy()方法,以允许bean在初始化和销毁bean时执行某些操作。
public class HelloLifeCycle implements InitializingBean, DisposableBean {
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet 启动");
}
public void destroy() throws Exception {
System.out.println("DisposableBean 停止");
}
}
2.bean自定义初始化方法和销毁方法,然后在定义bean时指定初始化方法和销毁方法
<bean id="helloLifeCycle" class="com.hzways.life.cycle.HelloLifeCycle" init-method="init3"
destroy-method="destroy3"/>
3.JSR-250 @PostConstruct和@PreDestroy注解通常被认为是在现代Spring应用程序中接PostConstruct注解用于方法上,该方法在初始化的依赖注入操作之后被执行。这个方法必须在class被放到service之后被执行,这个注解所在的类必须支持依赖注入。
public class HelloLifeCycle {
@PostConstruct
private void init2() {
System.out.println("PostConstruct 启动");
}
@PreDestroy
private void destroy2() {
System.out.println("PreDestroy 停止");
}
}
Note:注意
PostConstruct注解用于方法上,该方法在初始化的依赖注入操作之后被执行。因此,容器必须要开启支持注解形式的依赖注入的功能,加入如下配置即可:
<context:annotation-config/>
如果一个bean同时实现上述三种形式,调用顺序为:
- 创建bean时
1. @PostConstruct 注解方式
2. InitializingBean 实现接口方式
3. custom init() 自定义初始化方法方式
- 销毁bean时
1. @PreDestroy 注解方式
2. DisposableBean 实现接口方式
3. custom destroy() 自定义销毁方法方式
Spring bean的生命周期
Startup and shutdown callbacks
结合生命周期机制,生命周期接口定义了任何具有自身生命周期需求的对象的基本方法(例如,启动和停止一些后台过程):
Lifecycle 生命周期回调钩子
public interface Lifecycle {
/**
* 启动当前组件
* <p>如果组件已经在运行,不应该抛出异常
* <p>在容器的情况下,这会将 开始信号 传播到应用的所有组件中去。
*/
void start();
/**
* (1)通常以同步方式停止该组件,当该方法执行完成后,该组件会被完全停止。当需要异步停
* 止行为时,考虑实现SmartLifecycle 和它的 stop(Runnable) 方法变体。
注意,此停止通知在销毁前不能保证到达:在*常规关闭时,{@code Lifecycle} bean将首先收到一个停止通知,然后才传播*常规销毁回调;然而,在*上下文的生命周期内的热刷新或中止的刷新尝试上,只调用销毁方法
对于容器,这将把停止信号传播到应用的所有组件*
*/
void stop();
/**
* 检查此组件是否正在运行。
* 1. 只有该方法返回false时,start方法才会被执行。
* 2. 只有该方法返回true时,stop(Runnable callback)或stop()方法才会被执行。
*/
boolean isRunning();
}
LifeCycle定义Spring容器对象的生命周期,任何spring管理对象都可以实现该接口。
然后,当ApplicationContext本身接收启动和停止信号(例如在运行时停止/重启场景)时,spring容器将在容器上下文中找出所有实现了LifeCycle及其子类接口的类,并一一调用它们实现的类。spring是通过委托给生命周期处理器LifecycleProcessor来实现这一点的。
LifecycleProcessor 生命周期处理器
请注意,
LifecycleProcessor本身就是LifeCycle接口的扩展。它还添加了另外两个方法来响应spring容器上下文的刷新(onRefresh)和关闭(close)。
public interface LifecycleProcessor extends Lifecycle {
/**
* 响应Spring容器上下文 refresh
*/
void onRefresh();
/**
* 响应Spring容器上下文 close
*/
void onClose();
}
Lifecycle 生命周期的不足
常规的
LifeCycle接口只是在容器上下文显式的调用start()/stop()方法时,才会去回调LifeCycle的实现类的startstop方法逻辑。并不意味着在上下文刷新时自动启动。
我们可以定义一个类实现Lifecycle
public class HelloLifeCycle implements Lifecycle {
private volatile boolean running = false;
public HelloLifeCycle() {
System.out.println("构造方法!!!");
}
@Override
public void start() {
System.out.println("lifycycle start");
running = true;
}
@Override
public void stop() {
System.out.println("lifycycle stop");
running = false;
}
@Override
public boolean isRunning() {
return running;
}
}
写一个测试类并在main方法里面启动spring容器,这里没有显示的调用context的start()和close()方法
public static void main(String[] args) throws InterruptedException {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath*:services.xml");
}
结果是,控制台没有任何输出,容器并没有调用生命周期的回调方法.
当我们将启动容器的类,显式的加上start和stop方法后:
public static void main(String[] args) throws InterruptedException {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath*:services.xml");
applicationContext.start();
applicationContext.close();
}
这时我们看控制台,spring容器回调了生命周期的方法

SmartLifecycle 自动的生命周期扩展
那么,如果Spring容器上下文没有显式的调用
start和destory(或者close,stop)等方法时,我们也需要做到生命周期回调,怎么做?
于是SmartLifecycle可以做到这一点,它继承自Lifecycle接口,新增了如下几个方法:
public interface SmartLifecycle extends Lifecycle, Phased {
/**
* 如果该`Lifecycle`类所在的上下文在调用`refresh`时,希望能够自己自动进行回调,则返回`true`* ,
* false的值表明组件打算通过显式的start()调用来启动,类似于普通的Lifecycle实现。
*/
boolean isAutoStartup();
/**
* Indicates that a Lifecycle component must stop if it is currently running.
* <p>The provided callback is used by the {@link LifecycleProcessor} to support
* an ordered, and potentially concurrent, shutdown of all components having a
* common shutdown order value. The callback <b>must</b> be executed after
* the {@code SmartLifecycle} component does indeed stop.
* <p>The {@link LifecycleProcessor} will call <i>only</i> this variant of the
* {@code stop} method; i.e. {@link Lifecycle#stop()} will not be called for
* {@code SmartLifecycle} implementations unless explicitly delegated to within
* the implementation of this method.
* @see #stop()
* @see #getPhase()
*/
void stop(Runnable callback);
}
SmartLifecycle用于细粒度控制特定bean的自动启动(包括启动阶段)。另外,请注意,stop通知在销毁前不能保证到达,在常规关闭时,所有生命周期bean将首先收到停止通知,然后才传播一般的销毁回调;但是,在上下文生命周期内的热刷新或中止刷新尝试时,只调用销毁方法。
容器中实现了Lifecycle的多个类如果希望有顺序的进行回调时,那么启动和关闭调用的顺序可能很重要。如果任何两个对象之间存在依赖关系,那么依赖方将在依赖后开始,在依赖前停止。然而,有时直接依赖关系是未知的。您可能只知道某个类型的对象应该在另一个类型的对象之前开始。在这些情况下,SmartLifecycle接口定义了另一个选项,即在其超接口上定义的getPhase()方法。
当开始时,getPhase()返回值最小的对象先开始,当停止时,遵循相反的顺序。因此,实现SmartLifecycle的对象及其getPhase()方法返回Integer.MIN_VALUE将在第一个开始和最后一个停止。相反,MAX_VALUE将指示对象应该在最后启动并首先停止(可能是因为它依赖于要运行的其他进程)。
SmartLifecycle对象的默认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()一样,但它将在上下文关闭时发生。另一方面,refresh回调支持SmartLifecycle bean的另一个特性。当刷新上下文(在所有对象实例化和初始化之后)时,将调用这个回调,此时,默认的生命周期处理器将检查每个SmartLifecycle对象的isAutoStartup()方法返回的布尔值。如果是true,那么该对象将在此时启动,而不是等待上下文或其自己的start()方法的显式调用(与上下文刷新不同,上下文启动不会自动发生在标准上下文实现中)。“阶段”值以及任何“依赖”关系将以如上所述的方式确定启动顺序。
总结
Lifecycle接口是为启动或停止生命周期,控制定义方法的通用接口。
*这方面的典型用例是控制异步处理。
注意:此接口不包含特定的auto-startup(自动启动) 语义。
考虑为此目的实现{@link SmartLifecycle}。
可以由两个组件(通常是在* Spring上下文中定义的Spring bean)和容器(通常是Spring {@link ApplicationContext} *本身)实现。
容器将把开始/停止信号传播到每个容器中*应用的所有组件,例如在运行时的停止/重启场景。
可用于直接调用或通过JMX进行管理操作。在后一种情况下,{@link org.springframework.jmx.export。mbeanexports} 通常是用一个 {@link org.springframework.jmx.export.assembler来定义的。InterfaceBasedMBeanInfoAssembler} *将活动控制的组件的可见性限制在Lifecycle 接口。 * *
注意,生命周期接口仅支持top-level 并且单例的 bean 。
在任何其他组件上,生命周期接口将保持未被检测到的*,因此将被忽略。
另外,请注意扩展的{@link SmartLifecycle}接口*提供了与应用程序上下文的启动和关闭阶段的集成
下期文章:
Shutting down the Spring IoC container gracefully in non-web applications
Spring-IOC bean 生命周期之 Lifecycle 钩子的更多相关文章
- Spring中Bean生命周期
Spring中的bean生命周期是一个重要的点,只有理解Bean的生命周期,在开发中会对你理解代码是非常有用的.对于Bean的周期,个人认为可以分为四个阶段.第一阶段:Bean的实例化,在该阶段主要是 ...
- Spring - IoC(10): 生命周期
Spring 容器可以管理 singleton 作用域 Bean 的生命周期,容器能够跟踪 Bean 实例的创建.销毁.管理 Bean 生命周期行为主要有两个时机: 注入 Bean 的依赖关系之后 即 ...
- Spring:Bean生命周期
关于Bean生命周期,我在网上找了两张图: 图1: 图2: 总结起来就是: Bean构建: Bean对象创建 > @Autowired | @Resource> @PostConstruc ...
- 2.Spring的Bean生命周期和组装方式
1.Spring IoC容器概述 Spring IoC容器: Spring容器即体现了IoC原理 Spring容器通过读取配置元数据负责对Beans实例化.配置和装配 配置元数据可以用X ...
- spring学习笔记(四)我对spring中bean生命周期的理解
我相信大部分同学对spring中bean的生命周期都不陌生,但是如果要详细的说出每一个步骤,可能能说出来的也不多,我之前也是这样,前几天调了一下spring的源码,看了一点书,突然一下明朗了,理解了s ...
- spring注解-bean生命周期
https://www.jianshu.com/p/70b935f2b3fe bean的生命周期 bean创建---初始化----销毁的过程 容器管理bean的生命周期 对象创建:容器启动后调用bea ...
- Spring的Bean生命周期理解
首先,在经历过很多次的面试之后,一直不能很好的叙述关于springbean的生命周期这个概念.今日对于springBean的生命周期进行一个总结. 一.springBean的生命周期: 如下图所示: ...
- Spring 的 Bean 生命周期,11 张高清流程图及代码,深度解析
在网上已经有跟多Bean的生命周期的博客,但是很多都是基于比较老的版本了,最近吧整个流程化成了一个流程图.待会儿使用流程图,说明以及代码的形式来说明整个声明周期的流程.注意因为代码比较多,这里的流程图 ...
- Spring配置文件-Bean生命周期配置(init-method方法,destory-method方法)
1.UserDaoImpl类 public class UserDaoImpl implements UserDao { public UserDaoImpl(){ System.out.printl ...
随机推荐
- EF6 CodeFirst代码迁移笔记
由于EF7只支持codefirst only.朕无奈被微软逼上了梁山学一下codefirst,就算是为明年做准备吧.写的这些网上大致都有,基本没啥 新内容, 迁移 使用自动迁移 Enable- ...
- 南阳nyoj 56 阶乘因式分解(一)
阶乘因式分解(一) 时间限制:3000 ms | 内存限制:65535 KB 难度:2 描述 给定两个数m,n,其中m是一个素数. 将n(0<=n<=10000)的阶乘分解质因数, ...
- C#设计模式六大原则——接口隔离
接口隔离定义: 一般有两种定义: 1:客户端不应该依赖他不需要的接口 2:类间的依赖关系应该建立在最小的接口上 我们在建立接口时,应该尽量建立单一,不臃肿庞大的接口,应尽量使接口细化,方法尽量 ...
- JVM之---Java源码编译机制
Sun JDK中采用javac将Java源码编译为class文件,这个过程包含三个步骤: 1.分析和输入到符号表(Parse and Enter) Parse过程所做的工作有词法和语法分 ...
- Swift 函数调用到底写不写参数名
最近真正开始学 Swift,在调用函数的时候遇到一个问题:到底写不写函数名? 我们来看两个个例子: // 1 func test(a: Int, b: Int) ->Int { return a ...
- struts2 上传下载文件,异常处理,数据类型转换
一,web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app version=" ...
- js-权威指南学习笔记13
第十三章 Web浏览器中的JavaScript 1.在客户端JS中,window对象也是全局对象. 2.window对象中其中一个最重要的属性是document,它引用Document对象. 3.JS ...
- jQuery源码学习笔记一
学习jQuery源码,我主要是通过妙味视频上学习的.这里将所有的源码分析,还有一些自己弄懂过程中的方法及示例整理出来,供大家参考. 我用的jquery v2.0.3版本. var rootjQuery ...
- 在生产环境中安全执行更新删除SQL脚本的技巧
今天在生产环境上解决问题,由于广发银行的管理制度是开发公司是不允许确生产环境的,所以我们只能把要更新的语句发给运营中心,由运营中心的投产人员执行,我们则在旁边看着:在他执行的时候发现了一个很有趣的技巧 ...
- JS将秒换成时分秒实现代码 [mark]
将秒换成时分秒的方法有很多,在本文将为大家介绍下,使用js的具体的实现思路,有需要的朋友可以参考下,希望对大家有所帮助 http://www.jb51.net/article/41098.htm fu ...