<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 的啟動(dòng)流程

          共 10526字,需瀏覽 22分鐘

           ·

          2023-07-02 21:28

          點(diǎn)擊關(guān)注公眾號(hào),Java干貨 及時(shí)送達(dá) ??
          0.前言

          背景:最近有位開(kāi)發(fā)同學(xué)說(shuō)面試被問(wèn)到Spring Boot 的啟動(dòng)流程,以及被問(wèn)到Spring Boot 的嵌入式Web容器是什么時(shí)候加載的。如何加載的。是怎么無(wú)縫切換的。

          這些問(wèn)題,其實(shí)回答起來(lái)也是比較復(fù)雜的。我們今天就從 SpringApplication.run(EasyPaasAdminApplication.class, args);入口,逐漸向下看下執(zhí)行流程,來(lái)試著回答一下前面這兩個(gè)問(wèn)題。后面關(guān)于SpringBoot 的web容器可以無(wú)縫隨意切換為jetty,undertow..這個(gè)問(wèn)題的回答涉及到Spring Boot是如何設(shè)計(jì)WebServer的。我們后續(xù)專(zhuān)門(mén)講解一下。

          1. 執(zhí)行邏輯梳理

          一般我們SpringBoot 應(yīng)用的啟動(dòng)入口都是如下這種固定的寫(xiě)法,

          63eec0a54f6ea8d11e95635c88352e61.webp

          也可以是這樣

                
                ?public?static?void?main(String[]?args)?{
          ???SpringApplication?application?=?new?SpringApplication(MyApplication.class);
          ???//?...?customize?application?settings?here
          ???application.run(args)
          ??}

          但總之,都是使用SpringApplication 調(diào)用靜態(tài)方法

          此方法的注釋

          Static helper that can be used to run a SpringApplication from the specified source using default settings.

                
                ?public?static?ConfigurableApplicationContext?run(Class<?>?primarySource,?String...?args)?{
          ??return?run(new?Class<?>[]?{?primarySource?},?args);
          ?}

          跟過(guò)來(lái)就到這,可以看到注釋運(yùn)行Spring應(yīng)用程序,創(chuàng)建并刷新一個(gè)新的ApplicationContext。

          db227ca695b03bd12806abda993e1f42.webp5828bc5b4224162ad8882e0d920cbbc0.webp

          跟代碼到這兒其實(shí)我們對(duì)于SpringBoot 的基本啟動(dòng)流程已經(jīng)知道了。但是要解答什么時(shí)候啟動(dòng)的Tomcat 還需要繼續(xù)分析。

          d0f7452a5ca203afe3fd2c7a338742a2.webp

          到這兒我們就可以繼續(xù)下去,發(fā)現(xiàn)Spring Boot 啟動(dòng)WebServer。此處的WebServer我就不展開(kāi)了,可以點(diǎn)擊去就三個(gè)方法start ,stop,getPort。

          可以看出來(lái)Spring 在設(shè)計(jì)接口的時(shí)候還是很?chē)?yán)謹(jǐn)和精簡(jiǎn)。我們的核心脈絡(luò)是梳理SpringBoot 啟動(dòng)過(guò)程,并且回答Tomcat 是如何被啟動(dòng)的。

          1fb512171f828a71532e3a3d8e17f87b.webp

          我們可以看到WebServer 的實(shí)現(xiàn)目前內(nèi)置的有5種。其實(shí)Spring Boot 還有一個(gè)特性叫做 自動(dòng)裝配。

          這就是為什么5個(gè)實(shí)現(xiàn),我們最后啟動(dòng)的是Tomcat。此處也不做展開(kāi)。后面我專(zhuān)門(mén)搞一個(gè)解析SpringBoot 自動(dòng)裝配的文章。

          310928cbcd682785d000d5e11847b6f0.webp

          我們看一下內(nèi)部start 的TomcatWebServer的內(nèi)部實(shí)現(xiàn)。了解過(guò)Tomcat 源碼的同學(xué)看到這兒就基本明白了。

          5b24e34c32a0909a1e84e29cdc707ba3.webp

          好源碼跟進(jìn)過(guò)程我們到此結(jié)束,我們整理和總結(jié)一下。

          通過(guò)掃一遍源碼我們大概可以總結(jié)出來(lái)如下三個(gè)階段

          準(zhǔn)備階段、應(yīng)用上下文創(chuàng)建階段、刷新上下文階段。

          1. 準(zhǔn)備階段: Spring Boot 會(huì)加載應(yīng)用程序的初始設(shè)置,并創(chuàng)建 Spring Boot 上下文。這個(gè)階段的核心源碼是 SpringApplication 類(lèi)的 run() 方法,它會(huì)調(diào)用 Spring Boot 的各個(gè)初始化器進(jìn)行初始化和準(zhǔn)備工作。

          2. 應(yīng)用上下文創(chuàng)建階段 : Spring Boot 會(huì)創(chuàng)建應(yīng)用程序的上下文,包括各種配置信息、Bean 的加載和初始化等。這個(gè)階段的核心源碼是 Spring Boot 自動(dòng)配置機(jī)制,通過(guò)掃描 classpath 中的配置文件,自動(dòng)加載和配置各種組件和 Bean。

          3. 刷新上下文階段: Spring Boot 會(huì)執(zhí)行各種啟動(dòng)任務(wù),包括創(chuàng)建 Web 服務(wù)器、加載應(yīng)用程序的配置、初始化各種組件等。這個(gè)階段的核心源碼是 Spring Boot 的刷新機(jī)制,它會(huì)調(diào)用各種初始化器和監(jiān)聽(tīng)器,執(zhí)行各種啟動(dòng)任務(wù)。其中啟動(dòng)Tomcat 就是在這個(gè)環(huán)節(jié)進(jìn)行。

          2. 核心源碼解析

          既然上面我們已經(jīng)基本上總結(jié)除了,Spring Boot的啟動(dòng)脈絡(luò)。也梳理出了一些核心源碼。那么我們對(duì)啟動(dòng)過(guò)程的核心源碼解析一下。

          2.1. 準(zhǔn)備階段

          在準(zhǔn)備階段中,Spring Boot 會(huì)加載應(yīng)用程序的初始設(shè)置,并創(chuàng)建 Spring Boot 上下文。這個(gè)階段的核心源碼是 SpringApplication 類(lèi)的 run() 方法,它會(huì)調(diào)用 Spring Boot 的各個(gè)初始化器進(jìn)行初始化和準(zhǔn)備工作。

                
                public?ConfigurableApplicationContext?run(String...?args)?{
          ?????????????????//?啟動(dòng)計(jì)時(shí)器
          ????????StopWatch?stopWatch?=?new?StopWatch();
          ????????stopWatch.start();

          ?????????????????//?定義應(yīng)用程序上下文和異常報(bào)告器列表
          ????????ConfigurableApplicationContext?context?=?null;
          ????????Collection<SpringBootExceptionReporter>?exceptionReporters?=?new?ArrayList<>();

          ?????????????????//?配置?Headless?屬性
          ????????configureHeadlessProperty();

          ?????????????????//?獲取?Spring?Boot?啟動(dòng)監(jiān)聽(tīng)器
          ????????SpringApplicationRunListeners?listeners?=?getRunListeners(args);
          ?????????????????//?執(zhí)行啟動(dòng)監(jiān)聽(tīng)器的?starting?方法
          ????????listeners.starting();

          ????????try?{
          ?????????????????//?解析命令行參數(shù)
          ????????????ApplicationArguments?applicationArguments?=?new?DefaultApplicationArguments(args);
          ?????????????????//?準(zhǔn)備應(yīng)用程序環(huán)境
          ????????????ConfigurableEnvironment?environment?=?prepareEnvironment(listeners,?applicationArguments);
          ?????????????????//?配置忽略?BeanInfo
          ????????????configureIgnoreBeanInfo(environment);
          ?????????????????//?打印?Banner
          ????????????Banner?printedBanner?=?printBanner(environment);
          ?????????????????//?創(chuàng)建應(yīng)用程序上下文
          ????????????context?=?createApplicationContext();
          ?????????????????//?獲取異常報(bào)告器,關(guān)于異常報(bào)告,我下次專(zhuān)門(mén)講一下SpringBoot 的異常收集器。
          ????????????exceptionReporters?=?getSpringFactoriesInstances(SpringBootExceptionReporter.class,?new?Class[]{ConfigurableApplicationContext.class},?context);
          ?????????????????//?準(zhǔn)備應(yīng)用程序上下文
          ????????????prepareContext(context,?environment,?listeners,?applicationArguments,?printedBanner);
          ?????????????????//?刷新應(yīng)用程序上下文
          ????????????refreshContext(context);
          ?????????????????//?刷新后操作
          ????????????afterRefresh(context,?applicationArguments);
          ?????????????????//?停止計(jì)時(shí)器
          ????????????stopWatch.stop();
          ?????????????????//?記錄啟動(dòng)日志
          ????????????if?(this.logStartupInfo)?{
          ????????????????new?StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(),?stopWatch);
          ????????????}
          ?????????????????//?執(zhí)行啟動(dòng)監(jiān)聽(tīng)器的?started?方法
          ????????????listeners.started(context);
          ?????????????????//?執(zhí)行?Runner
          ????????????callRunners(context,?applicationArguments);
          ????????}?catch?(Throwable?ex)?{
          ?????????????????//?處理啟動(dòng)失敗
          ????????????handleRunFailure(context,?ex,?exceptionReporters,?listeners);
          ????????????throw?new?IllegalStateException(ex);
          ????????}

          ????????try?{
          ?????????????????//?執(zhí)行啟動(dòng)監(jiān)聽(tīng)器的?running?方法
          ????????????listeners.running(context);
          ????????}?catch?(Throwable?ex)?{
          ?????????????????//?處理啟動(dòng)失敗
          ????????????handleRunFailure(context,?ex,?exceptionReporters,?null);
          ????????????throw?new?IllegalStateException(ex);
          ????????}

          ?????????????????//?返回應(yīng)用程序上下文
          ????????return?context;
          ????}

          run() 方法中,Spring Boot 首先會(huì)創(chuàng)建一個(gè) StopWatch 對(duì)象,用于記錄整個(gè)啟動(dòng)過(guò)程的耗時(shí)。然后,Spring Boot 會(huì)調(diào)用 getRunListeners(args) 方法獲取 Spring Boot 的各個(gè)啟動(dòng)監(jiān)聽(tīng)器,并調(diào)用starting() 方法通知這些監(jiān)聽(tīng)器啟動(dòng)過(guò)程已經(jīng)開(kāi)始。接著調(diào)用 prepareEnvironment(listeners, applicationArguments) 方法創(chuàng)建應(yīng)用程序的環(huán)境變量。

          這個(gè)方法會(huì)根據(jù)用戶的配置和默認(rèn)設(shè)置創(chuàng)建一個(gè) ConfigurableEnvironment對(duì)象,并將其傳給后面的 createApplicationContext() 方法。printBanner(environment) 方法打印啟動(dòng)界面的 Banner,調(diào)用 refreshContext(context)方法刷新上下文。

          這個(gè)方法會(huì)啟動(dòng)上下文,執(zhí)行各種啟動(dòng)任務(wù),包括創(chuàng)建 Web 服務(wù)器、加載應(yīng)用程序的配置、初始化各種組件等。具體的啟動(dòng)任務(wù)會(huì)在刷新上下文階段中進(jìn)行。

          2.2. 應(yīng)用上下文創(chuàng)建階段

          在應(yīng)用上下文創(chuàng)建階段中,Spring Boot 會(huì)創(chuàng)建應(yīng)用程序的上下文,包括各種配置信息、Bean 的加載和初始化等。這個(gè)階段的核心源碼是 Spring Boot 自動(dòng)配置機(jī)制,通過(guò)掃描 classpath 中的配置文件,自動(dòng)加載和配置各種組件和 Bean。

                
                protected?ConfigurableApplicationContext?createApplicationContext()?{
          ????Class<?>?contextClass?=?this.applicationContextClass;
          ????if?(contextClass?==?null)?{
          ????????try?{
          ????????????switch?(this.webApplicationType)?{
          ????????????????case?SERVLET:
          ????????????????????contextClass?=?Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
          ????????????????????break;
          ????????????????case?REACTIVE:
          ????????????????????contextClass?=?Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
          ????????????????????break;
          ????????????????default:
          ????????????????????contextClass?=?Class.forName(DEFAULT_CONTEXT_CLASS);
          ????????????}
          ????????}
          ????????catch?(ClassNotFoundException?ex)?{
          ????????????throw?new?IllegalStateException(
          ????????????????????"Unable?to?create?a?default?ApplicationContext,?"?+
          ????????????????????"please?specify?an?ApplicationContextClass",?ex);
          ????????}
          ????}
          ????return?(ConfigurableApplicationContext)?BeanUtils.instantiateClass(contextClass);
          }

          createApplicationContext() 方法中,Spring Boot 首先會(huì)判斷應(yīng)用程序的類(lèi)型,如果是 Web 應(yīng)用程序,則會(huì)創(chuàng)建一個(gè) WebApplicationContext;否則,會(huì)創(chuàng)建一個(gè)普通的 ApplicationContext。調(diào)用 BeanUtils.instantiateClass(contextClass) 方法創(chuàng)建應(yīng)用程序的上下文。這個(gè)方法會(huì)根據(jù)上面的邏輯創(chuàng)建一個(gè)相應(yīng)的 ApplicationContext。調(diào)用 load() 方法加載應(yīng)用程序的配置。

          關(guān)于加載應(yīng)用配置,可以參閱我之前寫(xiě)一篇文章《三分鐘了解SpringBoot配置優(yōu)先級(jí)底層源碼解析》。這個(gè)方法會(huì)掃描 classpath 中的各種配置文件,例如 application.properties、application.ymlMETA-INF/spring.factories 等,自動(dòng)配置各種組件和 Bean。調(diào)用 postProcessApplicationContext() 方法對(duì)應(yīng)用程序的上下文進(jìn)行后處理。這個(gè)方法會(huì)調(diào)用各種初始化器和監(jiān)聽(tīng)器,執(zhí)行各種初始化任務(wù)。

          https://blog.csdn.net/wangshuai6707/article/details/130966177

          2.3. 刷新上下文階段

          在刷新上下文階段中,Spring Boot 會(huì)執(zhí)行各種啟動(dòng)任務(wù),包括創(chuàng)建 Web 服務(wù)器(剛才我們跟源碼的時(shí)候也看到了,如上我的截圖)、加載應(yīng)用程序的配置、初始化各種組件等。這個(gè)階段的核心源碼是 Spring Boot 的刷新機(jī)制,它會(huì)調(diào)用各種初始化器和監(jiān)聽(tīng)器,執(zhí)行各種啟動(dòng)任務(wù)。

                
                protected?void?refreshContext(ConfigurableApplicationContext?applicationContext)?{
          ????refresh(applicationContext);
          ????if?(this.registerShutdownHook)?{
          ????????try?{
          ????????????applicationContext.registerShutdownHook();
          ????????}
          ????????catch?(AccessControlException?ex)?{
          ????????????//?Not?allowed?in?some?environments.
          ????????}
          ????}
          }

          refreshContext() 方法中調(diào)用 refresh(applicationContext) 方法刷新上下文。這個(gè)方法是 ApplicationContext 接口的核心方法,會(huì)啟動(dòng)上下文,執(zhí)行各種啟動(dòng)任務(wù)。調(diào)用 registerShutdownHook() 方法注冊(cè)應(yīng)用程序的關(guān)閉鉤子。這個(gè)方法會(huì)在應(yīng)用程序關(guān)閉時(shí)自動(dòng)執(zhí)行,清理資源、關(guān)閉線程等,所以我們利用此特性在服務(wù)關(guān)閉的時(shí)候清理一些資源。并向外部發(fā)送告警通知。

          refresh(applicationContext) 方法中,Spring Boot 會(huì)執(zhí)行上下文的各種啟動(dòng)任務(wù),包括創(chuàng)建 Web 服務(wù)器、加載應(yīng)用程序的配置、初始化各種組件等。具體的啟動(dòng)任務(wù)會(huì)調(diào)用各種初始化器和監(jiān)聽(tīng)器,例如:

                
                for?(ApplicationContextInitializer<?>?initializer?:?getInitializers())?{
          ????initializer.initialize(applicationContext);
          }

          另外,Spring Boot 還會(huì)調(diào)用各種監(jiān)聽(tīng)器,我們不做贅述,例如:

                
                for?(ApplicationListener<?>?listener?:?getApplicationListeners())?{
          ????if?(listener?instanceof?SmartApplicationListener)?{
          ????????SmartApplicationListener?smartListener?=?(SmartApplicationListener)?listener;
          ????????if?(smartListener.supportsEventType(eventType)
          ????????????????&&?smartListener.supportsSourceType(sourceType))?{
          ????????????invokeListener(smartListener,?event);
          ????????}
          ????}
          ????else?if?(supportsEvent(listener,?eventType))?{
          ????????invokeListener(listener,?event);
          ????}
          }

          基本上就是這些了。

          關(guān)于SpringApplication的官方文檔講的比較簡(jiǎn)單,大家可供參考。地址如下:

          • https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.spring-application
          5ed3b50d82e1d3ad481f9d2690fbd492.webp

          來(lái)源:blog.csdn.net/wangshuai6707/

          article/details/131006060

              
                
                  


                            
                              

          c1ba4365c0900f1d0a1f9403bda1f1ff.webp

                                


          簡(jiǎn)單、漂亮、容易上手的開(kāi)源 SAAS 多租戶快速開(kāi)發(fā)平臺(tái),已開(kāi)源

          你見(jiàn)過(guò)哪些目瞪口呆的 Java 代碼技巧?

          從3s到25ms!看看京東的接口優(yōu)化技巧,確實(shí)很優(yōu)雅??!

          12個(gè)超好用的免費(fèi)在線工具,大大提高生產(chǎn)力,建議收藏!

                                  

          最近面試BAT,整理一份面試資料 Java面試BATJ通關(guān)手冊(cè) ,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點(diǎn)“ 在看 ”,關(guān)注公眾號(hào)并回復(fù)? Java ?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。

          謝謝支持


          瀏覽 51
          點(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>
                  日本免费在线黄色视频 | 国产A片免费领取 | 欧美成人靠逼小视频 | 深夜福利久久 | 大香蕉毛片|