日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

SpringBoot運行流程源碼分析

上一章中我們分析了 SpringApplication 類實例化的源代碼,在此過程中完成了基本配置文件的加載和實例化。當 SpringApplication 對象被創建之后, 通過調用其 run 方法來進行SpringBoot 的啟動和運行,至此正式開啟了 SpringApplication 的生命周期。
本章介紹的內容同樣是 Spring Boot 運行的核心流程之一,我們將會圍繞 SpringApplicationRunListeners、ApplicationArguments、ConfigurableEnvironment 以及 應用上下文信息等部分展開講解。

run方法核心流程

在分析和學習整個 run 方法的源代碼及操作之前,我們先通過圖 4-1 所示的流程圖來看一下SpringApplication 調用的 run 方法處理的核心操作都包含哪些。然后,后面的章節我們再逐步細化分析每個過程中的源代碼實現。

SpringBoot運行流程源碼分析:run方法流程及監聽器

 

上面的流程圖可以看出,SpringApplication 在 run 方法中重 點做了以下操作。

.獲取監聽器和參數配置。

.打印 Banner 信息。

.創建并初始化容器。

監聽器發送通知。

當然,除了核心操作,run 方法運行過程中還涉及啟動時長統計、異常報告、啟動日志、異常處理等輔助操作。

對照流程圖,我們再來整體看一下入口 run 方法的源代碼,核心部分的功能已通過注釋的形式進行說明。

public ConfigurableApplicationContext run(String... args) {
//創建 stopwatch 對象, 用于統 i 計 run 方法啟動時長
StopWatch stopWatch = new StopWatch();
//啟動統計
stopwatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> except ionReporters = new Arraylis
t<>();
//配置 headless 屬性
configureHeadlessProperty();
//獲得 SpringAppl icat ionRunL istener 數組
//該數組封裝 FSpringAppl icat ionRunL isteners 對象的 L isteners 中
SpringApplicationRunListeners listeners = getRunListeners(args);
//啟動監聽,遍歷 SpringAppl icat ionRunL istener 數組每個元素,并執行
listeners .starting();
try {創建 Appl icat ionArguments 對象
ApplicationArguments applicationArguments = new DefaultApplicationArgum
ents(
args);
//加載屬性配置,包括所有的配置屬性(如: appl icat ion. properties 中和外部的屬
性配置)
Conf igurableEnvironment environment = prepareEnvironment(listeners,
applicationArg
uments);
configureIgnoreBeanInfo( environment);
//打 Banner
Banner printedBanner = printBanner ( environment);
//創建容器
context = createApplicationContext();
//異常報告器
exceptionReporters = getSpringFactoriesInstances(
Spr ingBootExcept ionReporter .class ,
new Class[] { ConfigurableApplicat ionContext.class }, context);
//準備容器,組件對象之間進行關聯
prepareContext(context, environment, listeners, applicationArguments, p
rintedBanner);
// 初始化容器
refreshContext(context);
//初始化操作之后執行,默認實現為空
afterRefresh( context, applicationArguments);
//停止時長統計
stopWatch. stop();
//打印啟動日志
if (this. logStartupInfo) {
new StartupInfoLogger(this . mainApplicat ionClass)
.logStarted(getApplicationLog(), stopwatch);
//通知監昕器:容器啟動完成
listeners . started( context);
//調用 Appl icat ionRunner 和 CommandL ineRunner 的運行方法。
callRunners (context, applicat ionArguments);
} catch (Throwable ex)//異常處理
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
try {
//通知監聽器:容器正在運行
listeners . running( context);
} catch (Throwable ex) {
// 異常處理
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
return context;
}

在整體了解了整個 run 方法運行流程及核心代碼后,下面我們針對具體的程進行講解。

SpringApplicationRunListener 監聽器

監聽器的配置與加載

讓我們忽略 Spring Boot 計 時和統計的輔助功能,直接來看 SpringApplicationRunListeners獲取和使用 SpringApplicationRunL isteners可以理解為一個 SpringApplicationRunListener的容器,它將 SpringApplicationRunListener 的集合以構造方法傳入, 并賦值給其 listeners成員變量,然后提供了針對 listeners 成員變量的各種遍歷操作方法,比如,遍歷集合并調用對應的 starting、started、 running 等方法。

SpringApplicationRunListeners 的構建很簡單,圖 4-1 中調用的 getRunListeners 方法也只是調用了它的構造方法。SpringApplication 中 getRunListeners 方法代碼如下。

private SpringApplicationRunListeners getRunListeners(String[] args) {
//構造 Class 數組
Class<?>[] types = new Class<?>[] { SpringApplication. class, String[].cla
/調用 SpringAppl icat ionRunL isteners 構造方法
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstan
ces
SpringApplicationRunListener .class, types, this, args));}
SpringApplicationRunListeners 構 造 方 法 的 第 二 個 參 數 便 是 SpringApplicationRunL
istener 的 集 合 , SpringApplication 中 調 用 構 造 方 法 時 該 參 數 是 通 過
getSpringFactoriesInstances 方法獲取的,代碼如下。
private <T> Collection<T> getSpringF actoriesInstances(Class<T> type,
Class<?>[] parameterTypes, object... args) {
//加 META- TNE/sprina. factori es 中對應監聽器的配
并將結果存 F'set 中(去重)
Set<Strine> names= newLinkedHashSet<>(
個命的能直,
SpringF actoriesl oader. loadFactoryNames(type, classloader));
/文憫化監聽器
List<T> instances = createSpringFactories Instances (type, parameterTypes,
classLoader, args, nam
排序
Annotat ionAwareOrderComparator .sort(instances);
eturn instances ; }

通過方法名便可得知,getSpringFactoriesInstances 是用來獲取 factories 配置文件中的注冊類,并進行實例化操作。

關于通過 SpringFactoriesL oader 獲取 META-INF/spring.factories 中對應的配置,前面章節已經多次提到,這里不再贅述。

SpringApplicationRunListener 的注冊配置位于 spring-boot 項目中的 spring.factories 文件內,Spring Boot 默認僅有- -個監聽器進行了注冊,關于其功能后面會專門講到。

# RunListeners
org. springframework. boot.SpringApplicationRunListener=
org. springframework. boot. context.event.EventPublishingRunL istener
我們繼續看實例化監聽器的方法 createSpringFactoriesInstances 的源代碼。
private <T> List<T> createSpringFactoriesInstances(Class<T> type
Class<?>[] parameterType
s, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names)
Class<?> instanceClass = ClassUtils . forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
//獲取有參構造器
Constructor<?> constructor = instanceClass . getDeclaredConstructor(par
ameterTypes);
T instance = (T) BeanUtils. instant iateClass(constructor, args);
instances . add(instance);
return instances;
}

在上面的代碼中,實例化監聽器時需要有一-個默認的構造方法, 且構造方法的參數為Class<?>[ ] parameterTypes。我們向上追蹤該參數的來源,會發現該參數的值為 Class 數組 , 數 組 的 內 容 依 次 為 SpringApplication.class 和 String[ ].class 。 也 就 是 說 ,SpringApplicationRunL istener 的實現類必須有默認的構造方法,且構造方法的參數必須依次為 SpringApplication 和 String[ ]類型。

SpringApplicationRunListener 源碼解析

接口 SpringApplicationRunListener 是 SpringApplication 的 run 方法監聽器。上節提到了SpringApplicationRunListener 通過 SpringFactoriesL oader 加載,并且必須聲明一個公共構造函數,該函數接收 SpringApplication 實例和 String[ ]的參數,而且每次運行都會創建一個新的實例。

SpringApplicationRunListener 提供了-系列的方法,用戶可以通過回調這些方法,在啟動各個流程時加入指定的邏輯處理。下面我們對照源代碼和注釋來了解一下該接口都定義了哪些待實現的方法及功能。

public interface SpringApplicationRunL istener {
// 當 run 方法第- 次被執行時,會被立即調用,可用于非常早期的初始化工作 default void
starting(){};
//當 environment 準備完成, 在 Appl icationContext 創建之前, 該方法被調用 default void
environmentPrepared(Conf igurableEnvironment environment) {};
//當 ApplicationContext 構建完成, 資源還未被加載時, 該方法被調用 default void
contextPrepared(ConfigurableApplicationContext context) {}
// 當 Appl icat ionContext 加 載 完 成 , 未 被 刷 新 之 前 , 該 方 法 被 調 用 default void
contextLoaded(ConfigurableApplicationContext context) {};
//當 ApplicationContext 刷新并啟動之后, CommandL ineRunner 和 Appl icat ionRunner 未
被調用之前, 該方法被調用
default void started(Conf igurableApplicationContext context) {};
//當所有準備工作就緒,run 方法執行完成之前, 該方法被調用
default void running(ConfigurableApplicationContext context) {};
//當應用程序出現錯誤時,該方法被調用
default void failed(ConfigurableApplicationContext context, Throwable exception) {};
}

我們通過源代碼可以看出,SpringApplicationRunListener 為 run 方法提供了各個運行階段的監聽事件處理功能。需要注意的是,該版本中的接口方法定義使用了 JAVA8 的新特性,方法已采用 default 聲明并實現空方法體,表示這個方法的默認實現,子類可以直接調用該方法,也可以選擇重寫或者不重寫。

圖 4-2 展示了在整個 run 方法的生命周期中 SpringApplicationRunListener 的所有方法所處的位置,該圖可以幫助我們更好地學習 run 方法的運行流程。在前面 run 方法的代碼中已經看到相關監聽方法被調用,后續的源代碼中也將涉及對應方法的調用,我們可參考此圖以便理解和加深記憶。

SpringBoot運行流程源碼分析:run方法流程及監聽器

 

實現類 EventPublishingRunListener

EventPublishingRunL istener 是 SpringBoot 中針對 SpringApplicationRunListener 接口的唯內建實現EventPublishingRunL istener使用內置的SimpleApplicationEventMulticaster來廣播在上下文刷新之前觸發的事件。

默認情況下,Spring Boot在初始化過程中觸發的事件也是交由EventPublishingRunListener來代理實現的。EventPublishingRunListener 的構造方法如下。

public EventPublishingRunListener(SpringApplication application, Stringa
rgs) {
this.application = application;
this.args = args;
//創建 SimpleAppl icat ionEventMulticaster/播器
this . initialMulticaster = new SimpleApplicationEventMulticaster();
//遍歷 Appl icat ionL istener 并關聯 S impleAppl icat ionEventMulticaster
for (ApplicationListener<?> listener : application. getListeners()) {
this. initialMulticaster . addApplicationListener(listener);
}

通過源代碼可以看出,該類的構造方法符合 SpringApplicationRunListener 所需的構造方法參數要求,該方法依次傳遞了 SpringApplication 和 String[ ]類型。在構造方法中初始化了該類的 3 個成員變量。

-application :類 型為 SpringApplication ,是當前運行的 SpringApplication 實例。

-args:啟動程序時的命令參數。

-initialMulticaster:類 型為 SimpleApplicationEventMulticaster,事件廣播器。

Spring Boot 完成基本的初始化之后,會遍歷 SpringApplication 的所有 ApplicationListener實 例 , 并 將 它 們 與 SimpleApplicationEventMulticaster 進 行 關 聯 , 方 便SimpleApplicationEvent-Multicaster 后續將事件傳遞給所有的監聽器。

EventPublishingRunListener 針對不同的事件提供了不同的處理方法,但它們的處理流程基本相同。

SpringBoot運行流程源碼分析:run方法流程及監聽器

 

下面我們根據圖 4-3 所示的流程圖梳理一下 整個事件的流程。

.程序啟動到某個步驟后,調用 EventPublishingRunListener 的某個方法。

EventPublishingRunListener 的具體方法將 application 參數和 args 參數封裝到對應的事件中。這里的事件均為 SpringApplicationEvent 的實現類。

.通過成員變量 initialMulticaster 的 multicastEvent 方法對事件進行廣播,或通過該方法的ConfigurableApplicationContext 參數的 publishEvent 方法來對事件進行發布。

.對應的 ApplicationListener 被觸發,執行相應的業務邏輯。

下面是 starting 方法的源代碼,可對照上述流程進行理解。該方法其他功能類似,代碼不再展示。

public void starting() {
this. initialMulticaster . multicastEvent(
new ApplicationStartingEvent (this. application, this.args));}

在上述源代碼中你是否發現-個問題,某些方法是通過 initialMulticaster 的 multicastEvent進行事件的廣播,某些方法是通過 context 參數的 publishEvent 方法來進行發布的。這是為什么呢?在解決這個疑問之前,我們先看一個比較特殊的方法 contextL oaded 的源代碼。

public void contextLoaded( ConfigurableApplicationContext context) {
//遍歷 application 中的所有監聽器實現類
for (ApplicationL istener<?> listener : this . application. getL isteners()) {
//如果為 Appl icationContextAware,則將上:下文信息設置到該監聽器內
if (listener instanceof Applicat ionContextAware) {
((ApplicationContextAware) listener). setApplicationContext( context);//將 application 中的監聽器實現類全部添加到上下文中
context . addApplicationL istener(listener);
// / "播事件 Appl icationPreparedEvent
this. initialMulticaster . multicastEvent (
new ApplicationPreparedEvent(this.application, this.args, context));
}

contextLoaded 方法在發布事件之前做了兩件事:第一,遍歷 application 的所有監聽器實現類,如果該實現類還實現了 ApplicationContextAware 接口,則將上下文信息設置到該監聽器內;第二,將 application 中的監聽器實現類全部添加到上下文中。最后一步才是調用事件廣播。

也正是這個方法形成了不同事件廣播形式的分水嶺,在此方法之前執行的事件廣播都是通過multicastEvent 來進行的,而該方法之后的方法則均采用 publishEvent 來執行。這是因為只有到了 contextL oaded 方法之后,上下文才算初始化完成,才可通過它的 publishEvent 方法來進行事件的發布。

自定義 SpringApplicationRunListener

上面我們一起學習了 SpringApplicationRunListener 的基本功能及實現類的源代碼,現在我們自定義-個 SpringApplicationRunListener 的實現類。通過在該實現類中回調方法來處理自己的業務邏輯。

自定義實現類比較簡單,可像通常實現一個接口一樣,先創建類 MyApplicationRunListener,實現接口 SpringApplicationRunListener 及其方法。然后在對應的方法內實現自己的業務邏輯,以下示例代碼中只簡單打印方法名稱。與普通接口實現唯一不同的是,這里需要指定一-個參數依次為 SpringApplication 和 String[ ]的構造方法,不然在使用時會直接報錯。

public class MyApplicationRunListener implements SpringApplicationRunListen
er {
public MyApplicationRunListener ( SpringApplication application, String[]
args){
System. out . println("MyApplicationRunListener constructed function");
@Override
public void starting() {
System. out . println("starting...");
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
System. out. println(" environmentPrepared...");
//在此省略掉其他方法的實現 }

當定義好實現類之后,像注冊其他監聽器一樣, 程序在 spring.factories 中進行注冊配置。如果項目中沒有 spring.factories 文件,也可在 resources 目錄下先創建 META-INF 目錄,然后在該目錄下創建文件 sprig.factories。

spring.factories 中配置格式如下。

# Run Listeners
org. springframework. boot . SpringApplicationRunListener=
com. secbro2. learn. listener . MyApplicationRunListener

啟動 Spring Boot 項目,你會發現在不同階段打印出不同的日志,這說明該實現類的方法已經被調用。

分享到:
標簽:SpringBoot
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定