spring4.1.8 扩展实战之四:感知 spring 容器变化 (SmartLifecycle 接口)
欢迎访问我的 GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本篇概览
本章是《spring4.1.8 扩展实战》的第四篇,如果业务上需要在 spring 容器启动和关闭的时候做一些操作,可以自定义 SmartLifecycle 接口的实现类来扩展,本章我们通过先分析再实战的方法,来掌握这种扩展方式;
往期扩展链接
前面三章已经做了一些扩展,地址如下:
本章概要
本章由以下几部分组成:
SmartLifecycle 接口概览;
spring 容器启动与 SmartLifecycle 的关系;
spring 容器关闭与 SmartLifecycle 的关系;
关于 Lifecycle 和 SmartLifecycle;
实战 SmartLifecycle 接口扩展;
SmartLifecycle 接口概览
先来看看 SmartLifecycle 接口的类图:
如上图所示,在继承了 Lifecycle 和 Phased 两个接口后,SmartLifecycle 一共定义了六个方法,为了便于后面的源码分析,先做个简介:
从上述列举中可以看出,感知容器变化的能力最终来自 Lifecycle,而 SmartLifecycle 只是 Lifecycle 的增强版,可以自定义优先级(getPhase),自主决定是否随容器启动(isAutoStartup),以及停止时能接受一个 runnable 对象(stop(Runnable));
spring 容器启动与 SmartLifecycle 的关系
现在可以结合 spring 源码来看看 SmartLifecycle 的使用场景,从 spring 容器初始化看起;
AbstractApplicationContext 类的 refresh 方法中,在 bean 的实例化和初始化操作完毕后,会调用 finishRefresh 方法,如下图红框所示:
finishRefresh 方法内容如下,中文注释对每个方法做了简介:
上述代码中,initLifecycleProcessor()和 getLifecycleProcessor().onRefresh()这两个方法和本章的主题有关,其他两个就不在本章展开了,我们从 initLifecycleProcessor 开始看起吧;
initLifecycleProcessor 方法的作用是为 applicationContext 的成员变量 lifecycleProcessor 赋值,如果已有名为"lifecycleProcessor"的 bean,lifecycleProcessor 就等于这个 bean,否则就实例化一个 DefaultLifecycleProcessor 对象,再让 lifecycleProcessor 等于这个对象,并且把这个对象作注册到 spring 环境中(名为"lifecycleProcessor"),源码如下:
接下来是==getLifecycleProcessor().onRefresh()==的执行,如果业务不自定义一个 LifecycleProcessor,就默认创建一个 DefaultLifecycleProcessor 对象,因此执行的就是 DefaultLifecycleProcessor 的 onRefresh 方法,来看看源码:
展开 startBeans 方法看看,==注意入参 autoStartupOnly 等于 true==:
SmartLifecycle 的实例的 start 被调用的地方是在 LifecycleGroup 内部,对应的方法是 doStart,如下所示,优先处理依赖 bean:
以上就是初始化阶段容器对 SmartLifecycle 实例的处理逻辑,简单的小结如下:
Lifecycle 的处理都是委托给 LifecycleProcessor 执行的,先准备好此实例;
将所有的 Lifecycle 实例按照 phase 分组;
从 phase 值最小的分组开始,依次执行其中每个 Lifecycle 对象的 start 方法;
关于容器启动时的 Lifecycle 的处理就分析到这里,接下来看看容器关闭时对 Lifecycle 操作;
spring 容器关闭与 SmartLifecycle 的关系
分析 SmartLifecycle 如何感知 spring 容器的关闭,首先要弄清楚 stop 方法的调用栈,从 LifecycleProcessor 接口看起吧:
如上所示,感知容器关闭只能靠 onClose 方法被调用了,去看看该方法的调用处;<br>
LifecycleProcessor 的 onClose 方法是在 AbstractApplicationContext 的 doClose 方法中被调用的,如下图红框所示,这是汇集了容器关闭时要执行的基本逻辑:
弄清了调用逻辑,可以去 DefaultLifecycleProcessor 中看看 SmartLifecycle 实例的 stop 方法是如何被调用的;
DefaultLifecycleProcessor 的 stop 方法中先调用 stopBeans 方法,再将成员变量 running 设置为 false,表示状态已不是运行中:
展开 stopBeans 方法:
上述代码和启动时执行 start 的逻辑基本相似,不同的是执行顺序正好相反;
看看 LifecycleGroup 的 stop 方法内部,是如何调用 Lifecycle 实例的 stop 方法的:
以上代码有一处需要注意:
SmartLifecycle 实例有个 stop(Runnable)方法,实现的时候可以在另一个线程中执行 stop 的逻辑,这样就可以多个 SmartLifecycle 实例并行执行 stop 逻辑了,可以提高执行速度,当前线程为了等待所有执行 stop 的线程,用了 CountDownLatch 来等待,为了避免无限期等待还设置了超时时间;
最后来看看 LifecycleGroup 的 stop 方法中循环调用的 doStop 方法吧,这里面才会真正的调用到 Lifecycle 实例的 stop 方法,还有上面我们分析的多线程逻辑:
从以上代码可以看出,SmartLifecycle 实现类的 stop(Runnable)被调用时,LifecycleGroup 已经将 stop 调用完毕后要做的工作通过 Runnable 传递给实现类了,因此实现类中要记得执行 Runnable 的 run 方法,否则会导致外部调用逻辑的参数不准备,影响调用线程的执行;
以上就是关闭容器阶段对 SmartLifecycle 实例的处理逻辑,简单的小结如下:
AbstractApplicationContext 的 doClose 方法在容器关闭时会被执行,此处调用 LifecycleProcessor 的 onClose 方法,由 LifecycleProcessor 负责所有 Lifecycle 实例的关闭操作;
将所有的 Lifecycle 实例按照 phase 分组;
从 phase 值最大的分组开始,依次执行其中每个 Lifecycle 对象的 stop 方法;
对每个 SmartLifecycle 实例,若想并行执行以加快 stop 执行速度,可以在 stop 方法中用新的线程来执行 stop 业务逻辑,但是最后不要忘记调用 Runnable 入参的 run 方法,以完成主线程的计数和统计;
主线程使用了 CountDownLatch,在调用了 SmartLifecycle 实例的 stop 方法后就会等待,等到计数达到 SmartLifecycle 总数或者等待超时,再继续向后执行;
关于容器启动时的 Lifecycle 的处理就分析到这里,接下来看看容器关闭时对 Lifecycle 操作;
Lifecycle 和 SmartLifecycle,自定义的时候用哪个?
看了上面的源码分析,我们对 Lifecycle 和 SmartLifecycle 有了更全面的认知,如果对执行顺序没有要求,在关闭的时候也没有性能或者时间要求,那么就用 Lifecycle 吧,因为更简单,如果在乎顺序,也期望关闭时多个 Lifecycle 实例能并行执行,快速结束,SmartLifecycle 无疑更适合;
理论上已经基本熟悉了,接下来通过一次实战来加深印象,我们自定义一个 SmartLifecycle 的实现类,并在 springboot 中验证以下;
实战 SmartLifecycle 接口扩展
本次实战的内容是创建一个 springboot 工程,在里面自定义一个 SmartLifecycle 接口的实现类,如果您不想敲代码,也可以去 github 下载源码,地址和链接信息如下表所示:
这个 git 项目中有多个文件夹,本章源码在文件夹 customizelifecycle 下,如下图红框所示:
接下来开始实战吧:
基于 maven 创建一个 springboot 的 web 工程,名为 customizelifecycle,pom.xml 如下:
创建 Utils.java,里面提供常用的静态方法,本次会用到的是 printTrack 方法,用来打印当前堆栈,便于我们观察程序执行情况:
创建自定义 SmartLifecycle 实现类 CustomizeLifeCycleLinstener.java,主要代码都有注释说明,就不多赘述了,前面咱们分析的几个调用方法都有日志打印,便于在执行的时候观察,另外需要注意的是 stop(Runnable)方法的实现中用了一个新的线程来执行关闭的逻辑,并且入参 Runnable 的 run 方法一定要调用:
启动类 CustomizelifecycleApplication.java 如下:
编码完毕,启动应用,日志如下(篇幅所限,前面那段 springboot 启动的常见日志去掉了):
上述日志可以看到 CustomizeLifeCycleLinstener 的日志输出和执行堆栈,与预期一致;
接下来验证关闭的逻辑了,有两种方式可以验证,第一种是将当前的应用做成 jar 包运行,在控制台输入"CTRL+C"即可触发容器关闭,还有一种更简单的,如果您用的是 IDEA 开发,那么请用 IDEA 将应用启动,关闭的时候点击下图红框中的按钮,即可触发容器关闭:
关闭日志如下所示:
如上述日志所示,由于 CustomizeLifeCycleLinstener 的 stop 方法中新建了一个线程来执行操作,因此日志中的堆栈是这个新线程的堆栈信息,如果您想看到主线程的调用堆栈,请去掉 new Thread 的代码再次运行即可;
至此,SmartLifecycle 接口的源码分析和自定义实战就全部结束了,对 spring 强大的扩展能力又多了一分认识,真心希望本文能助您在感知容器变化的开发中收获一些启发,当然,spring 中还有更多精彩的扩展等着我们去探索,下一篇咱们继续;
欢迎关注 InfoQ:程序员欣宸
版权声明: 本文为 InfoQ 作者【程序员欣宸】的原创文章。
原文链接:【http://xie.infoq.cn/article/fee7d60cc3f7c7859ee611151】。文章转载请联系作者。
评论