Spring 之 Lifecycle 及 SmartLifecycle

最近在看Eureka源碼,本想快速解決這場沒有硝煙的戰役,不曾想阻塞性問題一個接一個。為正確理解這個框架,我不得不耐著性子,慢慢梳理這些讓人困惑的點。譬如本章要梳理的LifecycleSmartLifecycle。它們均為接口,其中后者繼承于前者,他們的類圖如下所示:

關于Lifecycle,網絡平臺給出的解釋是這樣的:它是Spring框架中的一個基礎接口,用于簡化管理有狀態的組件(譬如連接池、定時任務等)的初始化、啟動、停止等生命周期過程。它定義了以下核心方法

  1. start():啟動組件。這通常涉及初始化必要的資源,使組件處于可操作狀態。
  2. stop():停止組件。執行必要的清理工作,釋放資源,使組件處于不可操作狀態。
  3. isRunning():該方法用于檢查組件當前是否正在運行。返回true表示組件已啟動且正在運行,返回false則表示未啟動或已停止。

與SmartLifecycle相比,Lifecycle接口較為簡單,不涉及啟動順序控制、自動啟動配置或生命周期回調等功能。它是更基礎的生命周期管理接口,適用于那些不需要復雜生命周期管理邏輯的組件

下面就一起看一下Spring的這個組件是如何使用的,其實非常簡單,就是實現這個接口且實現其中定義的接口方法,比如下面這個自定義實現類:

package org.com.chinasoft.lifecycle;import org.springframework.context.Lifecycle;
import org.springframework.context.annotation.Configuration;@Configuration
public class MyLifecycle implements Lifecycle {/*** A 組件的運行狀態*/private volatile boolean running = false;/*** 容器啟動后調用*/@Overridepublic void start() {System.out.println("lifecycle 容器啟動完成,啟動A組件...");running = true;}/*** 容器停止時調用*/@Overridepublic void stop() {System.out.println("lifecycle 收到關閉容器的信號,關閉A組件...");running = false;}/*** 檢查此組件是否正在運行。* 1. 只有該方法返回false時,start方法才會被執行。* 2. 只有該方法返回true時,stop(Runnable callback)或stop()方法才會被執行。*/@Overridepublic boolean isRunning() {System.out.println("lifecycle 檢查A組件的運行狀態:" + running);return running;}
}

這個類是如何使用的呢通過顯式調用ConfigurableApplicationContext上的start()/stop()方法實現的。具體可以看一下下面這個類:

package org.com.chinasoft;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication(exclude = KafkaAutoConfiguration.class)
public class EurekaServiceApplication {public static void main(String[] args) {ConfigurableApplicationContext ctx = SpringApplication.run(EurekaServiceApplication.class, args);ctx.start();ctx.stop();}}

為什么可以這樣呢?這是由于ConfigurableApplicationContext接口繼承了Lifecycle接口,關于這個接口的繼承結構可以看一下下面這幅圖:

從圖中可以很清楚的看到ConfigurableApplicationContext接口繼承了Lifecycle接口,由此所有實現該接口的類都擁有了Lifecycle的功能。就像案例中寫的那樣,為了演示Lifecycle接口的用法,我們顯式的調用ConfigurableApplicationContext對象上的start()和stop()方法。執行結果如下圖所示:

這里既然提到了顯式調用,那如果不顯式調用是不是會不一樣?(網絡資料是這樣講的:如果不顯式調用不會有圖片中的輸出)。注釋上述調用代碼后的執行結果如下圖所示:

個人覺得這里就有點意思了,實際應用過程中,我們會顯式調用這個嗎?如果不會,這個調用的邏輯又在哪里呢?為了了解這個過程,還是先來看一下顯式調用的執行流程(紅色加粗加下劃線的部分即為實際的執行流程):

  1. AbstractApplicationContext#start()【這個實現方法來自于其實現的Lifecycle接口】
  2. AbstractApplicationContext#getLifecycleProcessor()【調用這個方法的主要目的是獲取Lifecycleprocessor對象,這個對象的實際類型為DefaultLifecycleProcessor】
  3. DefaultLifecycleProcessor#start()【調用該類中的start()方法。這個start()方法接著會調用DefaultLifecycleProcessor類上的startBeans()方法。這個方法首先從容器中拿到所有實現Lifecycle的bean,然后遍歷這個集合,將拿到的bean對象包裝到Map<Integer, LifecycleGrop>集合中(這個集合的value是一個LifecycleGroup對象),接著會不斷調用LifecycleGroup對象的add()方法將Lifecycle對象的名字及對象添加到LifecycleGroup對象中實際類型為List的members集合中,注意這個集合中元素的實際類型為LifecycleGroupMember。最后遍歷Map<Integer, LifecycleGrop>集合,然后調用LifecycleGroup對象上名為start()的方法,這個方法首先判斷members元素是否為空,如果不為空則排序,然后遍歷集合對象中的元素(LifecycleGroupMember),這個遍歷會首先對要處理的元素進行檢查,看其是否在LifecycleGroup對象上的lifecycleBeans集合中,如果在,則調用DefaultLifecycleProcessor類上的doStart()方法,如果一切順利最后就調用Lifecycle實現類上的start()方法。這個處理方法的具體實現邏輯如下所示:
private void doStart(Map<String, ? extends Lifecycle> lifecycleBeans, String beanName, boolean autoStartupOnly) {Lifecycle bean = lifecycleBeans.remove(beanName);if (bean != null && !this.equals(bean)) {String[] dependenciesForBean = this.beanFactory.getDependenciesForBean(beanName);for (String dependency : dependenciesForBean) {doStart(lifecycleBeans, dependency, autoStartupOnly);}if (!bean.isRunning() &&(!autoStartupOnly || !(bean instanceof SmartLifecycle) || ((SmartLifecycle) bean).isAutoStartup())) {if (logger.isDebugEnabled()) {logger.debug("Starting bean '" + beanName + "' of type [" + bean.getClass() + "]");}try {bean.start();}catch (Throwable ex) {throw new ApplicationContextException("Failed to start bean '" + beanName + "'", ex);}if (logger.isDebugEnabled()) {logger.debug("Successfully started bean '" + beanName + "'");}}}
}

實際調用跟蹤過程中發現,這個方法中Lifecycle類型的對象bean是一個代理對象。難道說所有實現該Lifecycle接口的對象都是通過動態代理的方式注入到了Spring容器中?關于這個問題暫且不表,先來看一下這個處理過程中用到的幾個類:

  1. DefaultLifecycleProcessor,這是一個實現了LifecycleProcessor接口(這個接口繼承了Lifecycle接口)和BeanFactoryAware接口的類。就測試案例中的操作來說:容器中所有對Lifecycle對象的操作都是通過這個類實現的。個人理解這個類的主要作用是:處理Spring Bean的生命周期。(大模型給出的解釋:該類是一個實現LifecycleProcessor和BeanFactoryAware接口的Java類。LifecycleProcessor接口定義了在Spring應用程序上下文中處理生命周期方法的策略,BeanFactoryAware接口允許類在被Spring容器實例化時獲取對BeanFactory的引用。這個類的作用是處理Spring Bean的生命周期。)
  2. LifecycleGroup,它是DefaultLifecycleProcessor類中的一個私有內部類,其擁有的屬性非常清晰:int類型的phase、long類型的timeout、boolean類型的autoStartupOnly、int類型的smartMemberCount、Map<String, ? extneds Lifecycle>類型的lifecycleBeans以及List<LifecycleGroupMember>類型的members。其中members用于存放包裝了Lifecycle對象的LifecycleGroupMember對象。另外這個類上還有add(String name, Lifecycle bean)方法、start()方法和stop()方法。其中第一個方法用于將Lifecycle對象添加到members集合中,這個方法在添加前會判斷這個對象是否是SmartLifecycle類型,如果是則將smartMemberCount的值自增1。start()方法的作用是在真正調用Lifecycle對象的start()方法前,做一些特殊處理,比如校驗(this.members.isEmpty())、對集合進行排序(Collections.sort(this.members))、遍歷members集合(調用DefaultLifecycleProcessor類上的doStart()方法,觸發Lifecycle對象上start()方法的調用)。stop()方法的處理邏輯與start()方法基本類似,有興趣的可以看一下源碼。個人理解這個類的主要作用就是對Lifecycle類型的對象做個分組,以方便管理,實現一些特殊功能
  3. LifecycleGroupMember,它與LifecycleGroup類似,都是DefaultLifecycleProcessor類中的一個私有內部類,這個類實現了Comparable接口,可以實現排序。這個類中有兩個屬性,一個為String類型的name,一個是Lifecycle類型的bean,其中只有一個compareTo()方法,這個方法是實現排序的關鍵。個人理解這個類的作用就是讓系統中的Lifecycle對象具有排序功能

現在讓我們一起回過頭來看一下這個問題:實際應用過程中,我們會顯式調用這個嗎?如果不會,這個調用的邏輯又在哪里呢?注意這個問題中提到的顯式調用,說的是Lifecycle接口中的start()及stop(),為了尋找這兩個方法的調用起點,我們可以利用一下擁有強大查找功能的集成工具idea。通過idea的查找工具,我們可以發現調用Lifecycle接口中的start()及stop()方法的地方有以下幾個:

  1. AbstractDiscoveryLifecycle的第243行this.start():AbstractDiscoveryLifecycle類實現了DiscoveryLifecycle接口(這個接口繼承了SmartLifecycle接口,其中SmartLifecycle接口又繼承了Lifecycle接口)。因此這里調用的就是AbstractDiscoveryLifecycle類中的start()方法,而這個方法的源調用方即ApplicationListener接口實現類AbstractDiscoveryLifecycle中的onApplicationEvent()方法
  2. RestartEndpoint的第203行this.context.start():ConfigurableApplicationContext類對象是RestartEndpoint中的屬性,而這個類又實現了Lifecycle接口,所以這一行調用的其實就是ConfigurableApplicationContext類父類AbstractApplicationContext中的start()方法
  3. EurekaDiscoveryClientConfiguration的第76行this.autoRegistration.start():這個調用最終調用的是EurekaAutoServiceRegistration類中的start()方法(這個類實現了SmartLifecycle接口
  4. EurekaAutoServiceRegistration的第126行start():EurekaAutoServiceRegistration實現了SmartLifecycle接口,所以這個start()方法中做了一些自己的邏輯
  5. AbstractApplicationContext的第1303行getLifecycleProcessor().start():這個就是前面梳理的哪個處理邏輯
  6. DefaultLifecycleProcessor的第175行bean.start():這個前面也梳理過,調用的就是Lifecycle實現類上的start()方法

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/21830.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/21830.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/21830.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mysql的鎖(全局鎖)

文章目錄 mysql按照鎖的粒度分類全局鎖概念&#xff1a;全局鎖使用場景&#xff1a;全局鎖備份案例&#xff1a; mysql按照鎖的粒度分類 全局鎖 概念&#xff1a; 全局鎖就是對整個數據庫實例加鎖。MySQL 提供了一個加全局讀鎖的方法&#xff0c;命令是: Flush tables with…

java —— 文件的創建、刪除、查詢和重命名

文件的相關操作通過 File 類并引入路徑來實現&#xff0c;這里對文件的操作只是對文件本身的增、刪、查等&#xff0c;不包括對文件內容的修改。 一、創建文件/文件夾 public static void main(String[] args) {//創建文件File file1new File("D:\\android\\test\\a.jpg…

vue封裝url驗證方法

vue封裝url驗證方法 在 Vue 中&#xff0c;你可以封裝一個 URL 驗證的方法來驗證給定的 URL 是否有效。以下是一個示例代碼&#xff1a; export const validateUrl (url) > {const regex /^(https?:\/\/)?([\da-z.-])\.([a-z.]{2,6})([/\w .-]*)*/;return regex.test(…

排序算法——歸并排序以及非遞歸實現

一、歸并排序思想 歸并排序&#xff08;MERGE-SORT&#xff09;是建立在歸并操作上的一種有效的排序算法,該算法是采用分治法&#xff08;Divide andConquer&#xff09;的一個非常典型的應用。將已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每個子序列…

OkHttp,一個賊牛的Java工具包

在當今的網絡應用開發中,Java 作為一種成熟的編程語言,廣泛應用于服務器端和客戶端的開發。網絡請求作為應用開發中不可或缺的一部分,選擇一個高效、穩定的網絡庫尤為重要。OkHttp 就是這樣一款優秀的網絡庫,它為Java提供了簡單易用、功能強大的網絡請求能力。本文將向讀者…

關于編譯的一些思路和猜想

一、編譯原理的難度 編譯原理特別復雜&#xff0c;研究的是高級語言如何翻譯成匯編語言的過程。 二、編譯過程中一些思路 (一)語義識別的作用 語義識別指的是把一些無關字符忽略&#xff0c;把一些變量名保存在一起&#xff0c;把用空格隔開的關鍵字單獨放一起。 例如&#…

重新ysyx

一、克隆倉庫 1.創建ssh key ssh-keygen -t rsa cd ~/.ssh ls 查看里面是否有id_rsa id_rsa.pub ssh-keygen -t rsa -C "xiantong15834753336outlook.com" cat id_rsa.pub***********查看里面的內容&#xff0c;復制到下圖中綠色的按鈕 git init ssh -T g…

spark3.0.1版本查詢Hbase數據庫例子

需求背景 現有需求&#xff0c;需要采用spark查詢hbase數據庫的數據同步到中間分析庫&#xff0c;記錄spark集成hbase的簡單例子代碼 import org.apache.hadoop.hbase.HBaseConfiguration import org.apache.hadoop.hbase.client.{ConnectionFactory, Scan} import org.apach…

Marin說PCB之Max parallel知多少?

今天是個陽光明媚&#xff0c;萬里烏云的好日子。小編我一如既往地到家打開電腦準備看騰訊視頻的五十公里桃花塢的第四季&#xff0c;在看到汪蘇瀧汪臺說650電臺要解散的時候小編我差點也哭了。650電臺之于桃花塢就像樂隊的鼓手一樣&#xff0c;都是一個團隊的靈感啊&#xff0…

CSS中的長度單位詳解

在CSS中&#xff0c;長度單位是定義元素尺寸、間距、邊距等的重要工具。不同的長度單位具有不同的特性和使用場景。 絕對長度單位 絕對長度單位在所有設備和瀏覽器中表示相同的長度。這些單位包括&#xff1a; 1.像素&#xff08;px&#xff09; 像素是最常用的長度單位。一…

C語言分支和循環(2)

我的相關博客&#xff1a; C語言的分支與循環&#xff08;1&#xff09; 1.switch語句 除了 if 語句外&#xff0c;C語?還提供了 switch 語句來實現分?結構。 switch 語句是?種特殊形式的 的 if...else 結構&#xff0c;?于判斷條件有多個結果的情況。它把多重 else if…

非質量成本總結

非質量成本 非質量成本 定義 舉例 固定成本 不隨生產量或工作量變動而變動的成本 辦公室租賃費 可變成本 隨著生產量或工作變動而變動的成本 材料費 直接成本 可以直接計入某項目的成本 工人工資 間接成本 不能直接計入某項目而需要再幾個項目之間或在項目與職能部…

Linux基本指令3

Linux基本指令3 目錄 Linux基本指令3 一、Linux文件系統管理 二、Linux進程與服務管理

億發:制造型企業信息化規劃——從破冰到全面落地

在制造型企業中&#xff0c;信息化規劃的落地是一個復雜而關鍵的過程。盡管規劃和藍圖可能已經制定完畢&#xff0c;但如何成功地實施信息化才是關鍵所在。本文將詳細介紹制造型企業信息化規劃的落地過程&#xff0c;通過三個周期逐步推進&#xff0c;最終實現信息化與自動化的…

深度學習知識與心得

目錄 深度學習簡介 傳統機器學習 深度學習發展 感知機 前饋神經網絡 前饋神經網絡&#xff08;BP網絡&#xff09; 深度學習框架講解 深度學習框架 TensorFlow 一個簡單的線性函數擬合過程 卷積神經網絡CNN&#xff08;計算機視覺&#xff09; 自然語言處理NLP Wo…

OpenAI助手API接入-問答對自動生成

支持GPT-3.5-Turbo, GPT-4o, GPT-4-Turbo import json import openai from pathlib import Path import os client openai.OpenAI(base_urlbase_url, api_keyapi_key) file client.files.create( fileopen("H3.pdf", "rb"), purposeassistants ) …

HTTP 的三次握手

????? HTTP 的三次握手是指在建立 TCP 連接時&#xff0c;客戶端和服務器之間進行的三步握手過程。這個過程確保了雙方都能夠互相通信&#xff0c;并且同步了彼此的序列號和確認號。 概念&#xff1a; 第一次握手&#xff1a;客戶端發送一個 SYN&#xff08;同步…

2.1數據的表示和運算--進位制

2.數據的表示和運算 2.1進位制 &#x1f53a;問題&#xff1a;計算機采用二進制有什么優點&#xff1f; 答&#xff1a; 1.制造兩個穩態的物理器件較容易。 2.二進制的運算規則簡單。 3.便于用邏輯門電路實現運算。 4.二進制的0和1正好對應邏輯值真和假。 &#x1f53a;…

成功解決“ModuleNotFoundError: No Module Named ‘utils’”錯誤的全面指南

成功解決“ModuleNotFoundError: No Module Named ‘utils’”錯誤的全面指南 在Python編程中&#xff0c;遇到ModuleNotFoundError: No Module Named utils這樣的錯誤通常意味著Python解釋器無法找到名為utils的模塊。這可能是由于多種原因造成的&#xff0c;比如模塊確實不存…

念念不忘,必有回響 的 echo

念念不忘&#xff0c;必有回響 的 echo 念念不忘&#xff0c;必有回響 的 echo幾個示例更多信息 念念不忘&#xff0c;必有回響 的 echo echo命令用于在終端設備上輸出字符串或變量的值&#xff0c;類似于Python的print和C語言的printf&#xff0c;是Linux系統中最常用的命令…