<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot運行流程源碼分析:run方法流程及監(jiān)聽器

          共 10812字,需瀏覽 22分鐘

           ·

          2022-05-10 13:09

          SpringBoot運行流程源碼分析

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

          run方法核心流程

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


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

          .獲取監(jiān)聽器和參數(shù)配置。

          .打印 Banner 信息。

          .創(chuàng)建并初始化容器。

          監(jiān)聽器發(fā)送通知。

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

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

          public ConfigurableApplicationContext run(String... args) {
          //創(chuàng)建 stopwatch 對象, 用于統(tǒng) i 計 run 方法啟動時長
          StopWatch stopWatch = new StopWatch();
          //啟動統(tǒng)計
          stopwatch.start();
          ConfigurableApplicationContext context = null;
          Collection except ionReporters = new Arraylis
          t<>();
          //配置 headless 屬性
          configureHeadlessProperty();
          //獲得 SpringAppl icat ionRunL istener 數(shù)組
          //該數(shù)組封裝 FSpringAppl icat ionRunL isteners 對象的 L isteners 中
          SpringApplicationRunListeners listeners = getRunListeners(args);
          //啟動監(jiān)聽,遍歷 SpringAppl icat ionRunL istener 數(shù)組每個元素,并執(zhí)行
          listeners .starting();
          try {創(chuàng)建 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);
          //創(chuàng)建容器
          context = createApplicationContext();
          //異常報告器
          exceptionReporters = getSpringFactoriesInstances(
          Spr ingBootExcept ionReporter .class ,
          new Class[] { ConfigurableApplicat ionContext.class }, context);
          //準備容器,組件對象之間進行關聯(lián)
          prepareContext(context, environment, listeners, applicationArguments, p
          rintedBanner);
          // 初始化容器
          refreshContext(context);
          //初始化操作之后執(zhí)行,默認實現(xiàn)為空
          afterRefresh( context, applicationArguments);
          //停止時長統(tǒng)計
          stopWatch. stop();
          //打印啟動日志
          if (this. logStartupInfo) {
          new StartupInfoLogger(this . mainApplicat ionClass)
          .logStarted(getApplicationLog(), stopwatch);
          //通知監(jiān)昕器:容器啟動完成
          listeners . started( context);
          //調(diào)用 Appl icat ionRunner 和 CommandL ineRunner 的運行方法。
          callRunners (context, applicat ionArguments);
          } catch (Throwable ex)//異常處理
          handleRunFailure(context, ex, exceptionReporters, listeners);
          throw new IllegalStateException(ex);
          try {
          //通知監(jiān)聽器:容器正在運行
          listeners . running( context);
          } catch (Throwable ex) {
          // 異常處理
          handleRunFailure(context, ex, exceptionReporters, null);
          throw new IllegalStateException(ex);
          return context;
          }

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

          SpringApplicationRunListener 監(jiān)聽器

          監(jiān)聽器的配置與加載

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


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

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

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

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


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

          # RunListeners
          org. springframework. boot.SpringApplicationRunListener=\
          org. springframework. boot. context.event.EventPublishingRunL istener
          我們繼續(xù)看實例化監(jiān)聽器的方法 createSpringFactoriesInstances 的源代碼。
          private List createSpringFactoriesInstances(Class type
          Class[] parameterType
          s, ClassLoader classLoader, Object[] args,
          Set<String> names) {
          List 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;
          }

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


          SpringApplicationRunListener 源碼解析

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


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

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

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

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


          實現(xiàn)類
          EventPublishingRunListener

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

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

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

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

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

          -args:啟動程序時的命令參數(shù)。

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

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


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


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

          .程序啟動到某個步驟后,調(diào)用
          EventPublishingRunListener 的某個方法。


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

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

          .對應的 ApplicationListener 被觸發(fā),執(zhí)行相應的業(yè)務邏輯。

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

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

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

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

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

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

          自定義
          SpringApplicationRunListener

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

          自定義實現(xiàn)類比較簡單,可像通常實現(xiàn)一個接口一樣,先創(chuàng)建類 MyApplicationRunListener,實現(xiàn)接口
          SpringApplicationRunListener 及其方法。然后在對應的方法內(nèi)實現(xiàn)自己的業(yè)務邏輯,以下示例代碼中只簡單打印方法名稱。與普通接口實現(xiàn)唯一不同的是,這里需要指定一-個參數(shù)依次為 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...");
          //在此省略掉其他方法的實現(xiàn) }

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

          spring.factories 中配置格式如下。

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

          啟動 Spring Boot 項目,你會發(fā)現(xiàn)在不同階段打印出不同的日志,這說明該實現(xiàn)類的方法已經(jīng)被調(diào)用。

          本文給大家講解的內(nèi)容是run方法核心流程 SpringApplicationRunListener 監(jiān)聽器

          1. 下篇文章給大家講解的是初始化ApplicationArguments和初始化 ConfigurableEnvironment;

          2. 覺得文章不錯的朋友可以轉(zhuǎn)發(fā)此文關注小編;

          3. 感謝大家的支持!


          本文就是愿天堂沒有BUG給大家分享的內(nèi)容,大家有收獲的話可以分享下,想學習更多的話可以到微信公眾號里找我,我等你哦。

          瀏覽 46
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  激情动态图欧美 | 亚洲日韩Av无码中文字幕美国 | 亚洲精品久久久久久久久久久久久久 | 成人乱码一区二区三区 | 青娱乐av |