Spring的Lifecycle和SmartLifecycle,可以沒用過(guò),但不能不知道!
本文轉(zhuǎn)載自微信公眾號(hào)「程序新視界」,作者二師兄。轉(zhuǎn)載本文請(qǐng)聯(lián)系程序新視界公眾號(hào)。
前言
在使用Spring的過(guò)程中,我們通常會(huì)用@PostConstruct和@PreDestroy在Bean初始化或銷毀時(shí)執(zhí)行一些操作,這些操作屬于Bean聲明周期級(jí)別的。
那么,就存在一些遺漏的場(chǎng)景,比如我們想在容器本身的生命周期(比如容器啟動(dòng)、停止)的事件上做一些工作,很典型的就是Spring Boot中啟動(dòng)內(nèi)嵌的Web容器。該怎么辦?
這就需要用到Spring提供的另外一個(gè)接口Lifecycle。這篇文件就介紹一下Lifecycle接口,以及比它更聰明(Smart)的SmartLifecycle。
Lifecycle接口
Lifecycle是一個(gè)接口,它的作用是讓開發(fā)者可以在所有的bean都創(chuàng)建完成(getBean)之后執(zhí)行自己的初始化工作,或者在退出時(shí)執(zhí)行資源銷毀工作。
Lifecycle定義了三個(gè)方法,任何Bean實(shí)現(xiàn)了Lifecycle方法,當(dāng)ApplicationContext收到start、stop和restart等信號(hào)時(shí),就會(huì)調(diào)用對(duì)應(yīng)的方法。因此可以通過(guò)實(shí)現(xiàn)Lifecycle接口獲得容器生命周期的回調(diào),實(shí)現(xiàn)業(yè)務(wù)擴(kuò)展。
LifeCycle定義如下:
- public interface Lifecycle {
- void start();
- void stop();
- boolean isRunning();
- }
自定義Lifecycle實(shí)現(xiàn)類
首先我們來(lái)自定義一個(gè)類,實(shí)現(xiàn)Lifecycle接口,來(lái)看看具體的實(shí)踐效果:
- @Component
- public class MyLifeCycle implements Lifecycle {
- /**
- * 運(yùn)行狀態(tài)
- */
- private volatile boolean running = false;
- /**
- * 容器啟動(dòng)后調(diào)用
- */
- @Override
- public void start() {
- System.out.println("容器啟動(dòng)后執(zhí)行MyLifeCycle操作...");
- running = true;
- }
- /**
- * 容器停止時(shí)調(diào)用
- */
- @Override
- public void stop() {
- System.out.println("收到關(guān)閉容器的信號(hào)MyLifeCycle操作...");
- running = false;
- }
- /**
- * 檢查此組件是否正在運(yùn)行。
- * 1. 只有該方法返回false時(shí),start方法才會(huì)被執(zhí)行。
- * 2. 只有該方法返回true時(shí),stop(Runnable callback)或stop()方法才會(huì)被執(zhí)行。
- */
- @Override
- public boolean isRunning() {
- System.out.println("檢查MyLifeCycle組件的運(yùn)行狀態(tài):" + running);
- return running;
- }
- }
單純的將上述代碼添加的Spring Boot項(xiàng)目當(dāng)中,你會(huì)發(fā)現(xiàn)啟動(dòng)時(shí)并沒有打印出任何相關(guān)的日志,只有在關(guān)閉應(yīng)用時(shí)會(huì)打印出:
- 檢查MyLifeCycle組件的運(yùn)行狀態(tài):false
這是因?yàn)?,在SpringBoot或Spring應(yīng)用中如果沒有調(diào)用AbstractApplicationContext#start方法,只是實(shí)現(xiàn)了Lifecycle接口,是不會(huì)執(zhí)行Lifecycle接口中的啟動(dòng)方法和isRunning方法的。但在應(yīng)用退出時(shí)會(huì)執(zhí)行Lifecycle#isRunning方法判斷該Lifecycle是否已經(jīng)啟動(dòng),如果返回true則調(diào)用Lifecycle#stop()停止方法。
這個(gè)實(shí)例有一個(gè)很明顯的問題,那就是需要使用者顯式的調(diào)用容器的start()和stop()方法,Lifecycle的接口方法才會(huì)被執(zhí)行。
而在一般的項(xiàng)目中,我們很少這樣顯式的去調(diào)用,所以就需要一個(gè)更“聰明”的類來(lái)處理,這就是SmartLifecycle。
SmartLifecycle
SmartLifecycle繼承自Lifecycle,提供了更豐富的功能:第一,start()方法無(wú)需容器顯式調(diào)用就可以被執(zhí)行;第二,可以控制多SmartLifecycle實(shí)例的執(zhí)行順序。
先來(lái)看一下SmartLifecycle接口的源碼:
- public interface SmartLifecycle extends Lifecycle, Phased {
- int DEFAULT_PHASE = 2147483647;
- default boolean isAutoStartup() {
- return true;
- }
- default void stop(Runnable callback) {
- this.stop();
- callback.run();
- }
- default int getPhase() {
- return 2147483647;
- }
- }
可以看出該接口除了繼承Lifecycle接口外,還繼承了Phased。其中g(shù)etPhase方法便是來(lái)自Phased。也正是基于Phased接口的這個(gè)方法來(lái)控制SmartLifecycle的執(zhí)行順序的。
來(lái)看一下實(shí)例代碼:
- @Component
- public class MySmartLifecycle implements SmartLifecycle {
- private volatile boolean running = false;
- /**
- * 如果該`Lifecycle`類所在的上下文在調(diào)用`refresh`時(shí),希望能夠自己自動(dòng)進(jìn)行回調(diào),則返回`true`,
- * false的值表明組件打算通過(guò)顯式的start()調(diào)用來(lái)啟動(dòng),類似于普通的Lifecycle實(shí)現(xiàn)。
- */
- @Override
- public boolean isAutoStartup() {
- return true;
- }
- /**
- * SmartLifecycle子類的才有的方法,當(dāng)isRunning方法返回true時(shí),該方法才會(huì)被調(diào)用。
- * 很多框架中的源碼中,都會(huì)把真正邏輯寫在stop()方法內(nèi)。
- * 比如quartz和Redis的spring支持包。
- */
- @Override
- public void stop(Runnable callback) {
- System.out.println("MySmartLifecycle容器停止,執(zhí)行回調(diào)函數(shù)");
- stop();
- // 如果你讓isRunning返回true,需要執(zhí)行stop這個(gè)方法,那么就不要忘記調(diào)用callback.run()。
- // 否則在程序退出時(shí),Spring的DefaultLifecycleProcessor會(huì)認(rèn)為這個(gè)MySmartLifecycle沒有stop完成,程序會(huì)一直卡著結(jié)束不了,等待一定時(shí)間(默認(rèn)超時(shí)時(shí)間30秒)后才會(huì)自動(dòng)結(jié)束。
- callback.run();
- }
- /**
- * 1. 主要在該方法中啟動(dòng)任務(wù)或者其他異步服務(wù),比如開啟MQ接收消息<br/>
- * 2. 當(dāng)上下文被刷新(所有對(duì)象已被實(shí)例化和初始化之后)時(shí),將調(diào)用該方法,
- * 默認(rèn)生命周期處理器將檢查每個(gè)SmartLifecycle對(duì)象的isAutoStartup()方法返回的布爾值。
- * 如果為“true”,則該方法會(huì)被調(diào)用,而不是等待顯式調(diào)用自己的start()方法。
- */
- @Override
- public void start() {
- System.out.println("MySmartLifecycle容器啟動(dòng)完成 ...");
- running = true;
- }
- /**
- * 接口Lifecycle子類的方法,只有非SmartLifecycle的子類才會(huì)執(zhí)行該方法。<br/>
- * 1. 該方法只對(duì)直接實(shí)現(xiàn)接口Lifecycle的類才起作用,對(duì)實(shí)現(xiàn)SmartLifecycle接口的類無(wú)效。<br/>
- * 2. 方法stop()和方法stop(Runnable callback)的區(qū)別只在于,后者是SmartLifecycle子類的專屬。
- */
- @Override
- public void stop() {
- System.out.println("MySmartLifecycle容器停止 ...");
- running = false;
- }
- /**
- * 1. 只有該方法返回false時(shí),start方法才會(huì)被執(zhí)行。<br/>
- * 2. 只有該方法返回true時(shí),stop(Runnable callback)或stop()方法才會(huì)被執(zhí)行。
- */
- @Override
- public boolean isRunning() {
- System.out.println("MySmartLifecycle檢查運(yùn)行狀態(tài) ...");
- return running;
- }
- /**
- * 如果有多個(gè)實(shí)現(xiàn)接口SmartLifecycle的類,則這些類的start的執(zhí)行順序按getPhase方法返回值從小到大執(zhí)行。<br/>
- * 例如:1比2先執(zhí)行,-1比0先執(zhí)行。stop方法的執(zhí)行順序則相反,getPhase返回值較大類的stop方法先被調(diào)用,小的后被調(diào)用。
- *
- */
- @Override
- public int getPhase() {
- return 0;
- }
- }
關(guān)于每個(gè)方法的功能,注釋部分已經(jīng)明確說(shuō)明了,下面啟動(dòng)SpringBoot項(xiàng)目,打印日志如下:
- MySmartLifecycle檢查運(yùn)行狀態(tài) ...
- MySmartLifecycle容器啟動(dòng)完成 ...
關(guān)閉SpringBoot項(xiàng)目,打印日志如下:
- MySmartLifecycle檢查運(yùn)行狀態(tài) ...
- MySmartLifecycle容器停止,執(zhí)行回調(diào)函數(shù)
- MySmartLifecycle容器停止 ...
通過(guò)上述實(shí)例可以看出:如果一個(gè)Bean實(shí)現(xiàn)了SmartLifecycle接口,則會(huì)執(zhí)行啟動(dòng)方法。SmartLifecycle#isRunning判斷是否已經(jīng)執(zhí)行,返回false表示還未執(zhí)行,則調(diào)用SmartLifecycle#start()執(zhí)行。
當(dāng)關(guān)閉時(shí),同樣先檢查運(yùn)行狀態(tài),如果正在運(yùn)行,則執(zhí)行關(guān)閉操作。關(guān)閉時(shí),還可以處理對(duì)應(yīng)的回調(diào)函數(shù)。
其中,Phased返回值越小,優(yōu)先級(jí)越高。
小結(jié)
當(dāng)需要基于Spring容器的生命周期來(lái)處理一些邏輯時(shí),通常可以實(shí)現(xiàn)SmartLifecycle接口來(lái)完成。像Spring Cloud,Spring Boot中都有大量的實(shí)踐案例。所以,無(wú)論實(shí)戰(zhàn)或閱讀源碼,不了解Lifecycle相關(guān)接口,都是一種損失。本文的產(chǎn)生也是在遇到Spring Cloud集成Nacos的源碼中獲得的靈感。