<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>

          從代碼層面看spring boot啟動(dòng)過(guò)程

          共 4338字,需瀏覽 9分鐘

           ·

          2021-09-01 22:56

          前言

          我們都知道spring boot項(xiàng)目是通過(guò)main方法來(lái)啟動(dòng)運(yùn)行的,但是main方法執(zhí)行之后,spring boot都替我們完成了哪些操作,最終讓我們的服務(wù)成功啟動(dòng)呢?今天我們就來(lái)從源碼層面探討下這個(gè)問(wèn)題。

          spring boot啟動(dòng)過(guò)程

          在開(kāi)始之前,我們先看這樣一段代碼:

          @SpringBootApplication
          public class DailyNoteApplication {

              public static void main(String[] args) {
                  SpringApplication.run(DailyNoteApplication.classargs);
              }

          }

          上面的這段代碼就是我們最常見(jiàn)的spring boot啟動(dòng)的main方法,今天我們就從這個(gè)main方法開(kāi)始,進(jìn)入spring boot的世界。

          springApplication

          首先,在main方法內(nèi)部執(zhí)行了SpringApplication.run(DailyNoteApplication.class, args),這個(gè)方法有兩個(gè)入?yún)ⅲ粋€(gè)是項(xiàng)目主類(lèi)(當(dāng)前類(lèi))的class,另一個(gè)就是main方法的args。

          這里先說(shuō)下這個(gè)args參數(shù),我們都知道spring boot是支持以命令行的方式注入配置信息的,它的實(shí)現(xiàn)就是依賴(lài)于這個(gè)args參數(shù)的。如果你將args刪掉,項(xiàng)目也是可以正常啟動(dòng)的,只是你再也沒(méi)辦法通過(guò)命令行的方式注入配置了。關(guān)于這一塊,我們之前在通過(guò)k8s啟動(dòng)spring boot項(xiàng)目的時(shí)候踩過(guò)坑,發(fā)現(xiàn)注入的參數(shù)不起作用,最后發(fā)現(xiàn)就是少了args。

          run方法

          run方法內(nèi)部,首先實(shí)例化了一個(gè)springApplication對(duì)象,然后又調(diào)用了另一個(gè)run方法:

          springApplication實(shí)例化

          我們先看springApplication的實(shí)例化過(guò)程:

          前兩個(gè)this都是簡(jiǎn)單的賦值,這里暫時(shí)先不過(guò)多研究,第三個(gè)this這里的WebApplicationType.deduceFromClasspath()是判斷我們的服務(wù)器類(lèi)別,在spring boot中,有兩種服務(wù)器一種就是傳統(tǒng)的sevlet,也就是基于tomcat(其中一種)這種,另一種就是reactive,也就是我們前面分享的webflux這種流式服務(wù)器。

          緊接著是初始化ApplicationContextInitializerApplicationListener,這里主要是獲取他們的spring工程實(shí)例,方便后續(xù)創(chuàng)建他們的實(shí)例,為了保證主流程的連貫性,我們暫時(shí)不看其方法內(nèi)部實(shí)現(xiàn)。

          最后一個(gè)賦值操作是找出包含main方法的類(lèi)的className。

          run方法開(kāi)始執(zhí)行

          下面我們看下springApplication實(shí)例的 run方法內(nèi)部執(zhí)行過(guò)程:

          • 創(chuàng)建了一個(gè)StopWatch對(duì)象,并調(diào)用它的start方法

            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
          • 設(shè)置javajava.awt.headless值,如果已經(jīng)設(shè)置過(guò)就取系統(tǒng)設(shè)置的值,如果沒(méi)有設(shè)置,則設(shè)置為true。這個(gè)是設(shè)置java的無(wú)頭模式,啟用之后,可以用計(jì)算能力來(lái)處理可視化操作(類(lèi)似于用算力代替顯卡渲染能力)

            configureHeadlessProperty();
          • 獲取spring boot運(yùn)行監(jiān)聽(tīng)器

            SpringApplicationRunListeners listeners = getRunListeners(args);
            listeners.starting();
          • 解析控制臺(tái)參數(shù)(args),獲取應(yīng)用參數(shù)

            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
          • 配置需要忽略的bean信息,從源碼中我們可以看出了,如果我們沒(méi)有設(shè)置spring.beaninfo.ignore,spring boot會(huì)給他默認(rèn)true

            configureIgnoreBeanInfo(environment);
            //方法內(nèi)部實(shí)現(xiàn)
            private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
              if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
               Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.classBoolean.TRUE);
               System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
              }
             }
          • 打印banner信息,這個(gè)banner就是spring boot啟動(dòng)的時(shí)候打印的哪個(gè)logo,那個(gè)是支持自定義的

            Banner printedBanner = printBanner(environment);
          • 創(chuàng)建spring boot容器,這里創(chuàng)建的時(shí)候會(huì)根據(jù)我們應(yīng)用的不同,選擇不同的容器

            context = createApplicationContext();
          • 創(chuàng)建spring工廠實(shí)例

            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                 new Class[] 
            { ConfigurableApplicationContext.class }, context);
          • 準(zhǔn)備容器,這一步會(huì)進(jìn)行初始化操作,把環(huán)境設(shè)置、系統(tǒng)參數(shù)、banner注入到容器中,并把容器綁定到監(jiān)聽(tīng)器上

            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
          • 刷新容器,這里其實(shí)進(jìn)行了兩步操作,一個(gè)是給我們的spring boot綁定SpringContextShutdownHook鉤子函數(shù),有了這個(gè)函數(shù),我們就可以?xún)?yōu)雅地關(guān)閉spring boot了;另一個(gè)是刷新beanFactory,默認(rèn)情況下spring boot為我們創(chuàng)建的是GenericApplicationContext容器,初始化完后,所有的對(duì)象都被初始化在它的beanFactory,為了確保其他組件也能拿到beanFactory中的內(nèi)容,refreshContext方法內(nèi)還進(jìn)行了同步操作(直接copy給他們):

          refreshContext(context);

          從源碼中可以很明顯看出這一點(diǎn):

          • 刷新完成后會(huì)執(zhí)行afterRefresh方法,但是這個(gè)方法默認(rèn)情況下是空的

            afterRefresh(context, applicationArguments);
          • 停止秒表。這個(gè)秒表的作用應(yīng)該就是計(jì)時(shí)

            stopWatch.stop();
          • 調(diào)用監(jiān)聽(tīng)器started方法,這方法修改了容器的狀態(tài)。和前面starting方法不同的是,這個(gè)方法必須在beanfactory刷新后執(zhí)行:

            listeners.started(context);
          • 運(yùn)行容器中的runner,這里的runner主要有兩類(lèi),一類(lèi)是繼承ApplicationRunner的,一類(lèi)是繼承CommandLineRunner。我猜測(cè)這個(gè)應(yīng)該是為了方便我們實(shí)現(xiàn)更復(fù)雜的需求實(shí)現(xiàn)的,目前還沒(méi)用到過(guò),后面可以找時(shí)間研究下

            callRunners(context, applicationArguments);
          • 最后一步還是監(jiān)聽(tīng)器的操作。這個(gè)方法最后將容器的狀態(tài)改為ACCEPTING_TRAFFIC,表示可以接受請(qǐng)求

            listeners.running(context);

            到這里,spring boot就啟動(dòng)成功了。下面是整個(gè)run方法的源碼,雖然不長(zhǎng),但是我感覺(jué)讀起來(lái)還是有點(diǎn)吃力,想想自己模仿spring boot寫(xiě)的demo,真的是小巫見(jiàn)大巫。

          總結(jié)

          spring boot啟動(dòng)過(guò)程雖然看起來(lái)簡(jiǎn)單,用起來(lái)簡(jiǎn)單,但是當(dāng)我一行一行看源碼的時(shí)候,我覺(jué)得不簡(jiǎn)單,就好比老遠(yuǎn)看一棵大樹(shù),不就是一個(gè)直立的桿嘛,但是當(dāng)你抵近看的時(shí)候,你會(huì)發(fā)現(xiàn)樹(shù)干有樹(shù)杈,樹(shù)杈又有小樹(shù)杈,總之看起來(lái)盤(pán)根錯(cuò)節(jié)的,總是感覺(jué)看不到樹(shù)真實(shí)的樣子。不過(guò),隨著后面我們不斷地將spring boot的樹(shù)葉、小樹(shù)杈一一拿掉的時(shí)候,我相信我們會(huì)越來(lái)越清楚地看到spring boot這棵大樹(shù)真實(shí)的樣子。

          今天的內(nèi)容,其實(shí)如果有一張時(shí)序圖,看起來(lái)就比較友好了,但是由于時(shí)間的關(guān)系,今天來(lái)不及做了,我們明天爭(zhēng)取把時(shí)序圖搞出來(lái)。

          另外,后面我還會(huì)把今天一筆帶過(guò)的方法盡可能詳細(xì)地研究然后講解的,我的目標(biāo)就是由大到?。◤臉?shù)干到樹(shù)杈,最后到樹(shù)葉)地剖析spring boot的源碼,最后把spring boot的核心技術(shù)梳理清楚。好了,今天就先到這里吧!

          - END -


          瀏覽 38
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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综合人妻 | 在线观看免费视频a | 大香蕉综合 | 免费的18禁的又黄又涩的网站 | 91又大又粗又爽 |