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