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

























