springboot原理(核心原理、啟動(dòng)流程、執(zhí)行流程)

SpringBoot是一個(gè)快速開發(fā)框架,快速的將一些常用的第三方依賴整合(原理:通過Maven子父工程的方式),簡(jiǎn)化XML配置,全部采用注解形式,內(nèi)置Http服務(wù)器(Jetty和Tomcat),最終以java應(yīng)用程序進(jìn)行執(zhí)行,它是為了簡(jiǎn)化Spring應(yīng)用的創(chuàng)建、運(yùn)行、調(diào)試、部署等而出現(xiàn)的,使用它可以做到專注于Spring應(yīng)用的開發(fā),而無需過多關(guān)注XML的配置。

核心原理

1. SpringBoot核心通過Maven繼承依賴關(guān)系快速整合第三方框架

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.RELEASE</version>    
</parent>
<dependencies>
    <!-- SpringBoot 整合SpringMVC -->
    <!-- 我們依賴spring-boot-starter-web能夠幫我整合Spring環(huán)境 原理通過Maven子父工程 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

springboot 通過引用spring-boot-starter-web依賴,整合SpingMVC框架。當(dāng)你添加了相應(yīng)的starter模塊,就相當(dāng)于添加了相應(yīng)的所有必須的依賴包,包括spring-boot-starter(這是Spring Boot的核心啟動(dòng)器,包含了自動(dòng)配置、日志和YAML);spring-boot-starter-test(支持常規(guī)的測(cè)試依賴,包括JUnit、Hamcrest、Mockito以及spring-test模塊);spring-boot-starter-web (支持全棧式Web開發(fā),包括Tomcat和spring-webmvc)等相關(guān)依賴。

2. 基于SpringMVC無配置文件(純Java)完全注解化實(shí)現(xiàn)SpringBoot框架,Main函數(shù)啟動(dòng)。

@SpringBootApplication 
public class Application {
    //方式一
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    //方式二
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(MySpringConfiguration.class);
        app.run(args);
    }
    //方式三
    public static void main(String[] args) {
        new SpringApplicationBuilder()
            .sources(Parent.class)
            .child(Application.class)
            .run(args);
    }
}

springboot有三種方式啟動(dòng),都會(huì)在沒有web.xml配置文件的情況,通過java代碼操作整個(gè)SpringMVC的初始化過程,java代碼最終會(huì)生成class文件,內(nèi)置Tomcat就會(huì)加載這些class文件,當(dāng)所有程序加載完成后,項(xiàng)目就可以訪問了。

啟動(dòng)流程

springboot中只需要有@SpringBootApplication這個(gè)注解,有了它馬上就能夠讓整個(gè)應(yīng)用跑起來。實(shí)際上它只是一個(gè)組合注解,@Configuration配置類,@ComponentScan類,包掃描@EnableAutoConfiguration根據(jù)需求自動(dòng)加載相關(guān)的bean這三個(gè)注解。

啟動(dòng)流程如下:

1. 初始化監(jiān)聽器,以及添加到SpringApplication的自定義監(jiān)聽器。

2. 發(fā)布ApplicationStartedEvent事件,如果想監(jiān)聽ApplicationStartedEvent事件,你可以這樣定義:public class ApplicationStartedListener implements ApplicationListener,然后通過SpringApplication.addListener(..)添加進(jìn)去即可。

3. 裝配參數(shù)和環(huán)境,確定是web環(huán)境還是非web環(huán)境。

4. 裝配完環(huán)境后,就觸發(fā)ApplicationEnvironmentPreparedEvent事件。

5. 如果SpringApplication的showBanner屬性被設(shè)置為true,則打印啟動(dòng)的Banner。

6. 創(chuàng)建ApplicationContext,會(huì)根據(jù)是否是web環(huán)境,來決定創(chuàng)建什么類型的ApplicationContext。

7. 裝配Context的環(huán)境變量,注冊(cè)Initializers、beanNameGenerator等。

8. 發(fā)布ApplicationPreparedEvent事件。

9. 注冊(cè)springApplicationArguments、springBootBanner,加載資源等

10. 遍歷調(diào)用所有SpringApplicationRunListener的contextLoaded()方法。

11. 調(diào)用ApplicationContext的refresh()方法,裝配context beanfactory等非常重要的核心組件。

12. 查找當(dāng)前ApplicationContext中是否注冊(cè)有CommandLineRunner,如果有,則遍歷執(zhí)行它們。

13. 發(fā)布ApplicationReadyEvent事件,啟動(dòng)完畢,表示服務(wù)已經(jīng)可以開始正常提供服務(wù)了。通常我們這里會(huì)監(jiān)聽這個(gè)事件來打印一些監(jiān)控性質(zhì)的日志,表示應(yīng)用正常啟動(dòng)了。

SpringApplication是springboot的入口

SpringApplication是springboot驅(qū)動(dòng)spring應(yīng)用上下文的引導(dǎo)類,他的run()方法啟動(dòng)Spring應(yīng)用,實(shí)質(zhì)上是為Spring應(yīng)用創(chuàng)建并初始化Spring上下文。

執(zhí)行流程

image.png

1. 初始化監(jiān)聽器,以及添加到SpringApplication的自定義監(jiān)聽器。

2. 發(fā)布ApplicationStartedEvent事件,如果想監(jiān)聽ApplicationStartedEvent事件,你可以這樣定義:public class ApplicationStartedListener implements ApplicationListener,然后通過SpringApplication.addListener(..)添加進(jìn)去即可。

3. 裝配參數(shù)和環(huán)境,確定是web環(huán)境還是非web環(huán)境。

4. 裝配完環(huán)境后,就觸發(fā)ApplicationEnvironmentPreparedEvent事件。

5. 如果SpringApplication的showBanner屬性被設(shè)置為true,則打印啟動(dòng)的Banner。

6. 創(chuàng)建ApplicationContext,會(huì)根據(jù)是否是web環(huán)境,來決定創(chuàng)建什么類型的ApplicationContext。

7. 裝配Context的環(huán)境變量,注冊(cè)Initializers、beanNameGenerator等。

8. 發(fā)布ApplicationPreparedEvent事件。

9. 注冊(cè)springApplicationArguments、springBootBanner,加載資源等

10. 遍歷調(diào)用所有SpringApplicationRunListener的contextLoaded()方法。

11. 調(diào)用ApplicationContext的refresh()方法,裝配context beanfactory等非常重要的核心組件。

12. 查找當(dāng)前ApplicationContext中是否注冊(cè)有CommandLineRunner,如果有,則遍歷執(zhí)行它們。

13. 發(fā)布ApplicationReadyEvent事件,啟動(dòng)完畢,表示服務(wù)已經(jīng)可以開始正常提供服務(wù)了。通常我們這里會(huì)監(jiān)聽這個(gè)事件來打印一些監(jiān)控性質(zhì)的日志,表示應(yīng)用正常啟動(dòng)了。

SpringBoot會(huì)觸發(fā)其他的一些事件,這些事件按下列順序觸發(fā):

(1)ApplicationStartingEvent:項(xiàng)目剛啟動(dòng)時(shí)觸發(fā),此時(shí)除了注冊(cè)監(jiān)聽器和初始器之外,其他所有處理都沒有開始;

(2)ApplicationEnvironmentPreparedEvent:上下文得到環(huán)境信息之后觸發(fā),此時(shí)上下文創(chuàng)建還沒有創(chuàng)建;

(3)ApplicationPreparedEvent:bean的定義信息加載完成之后觸發(fā),此時(shí)bean還沒有初始化;

(4)ApplicationReadyEvent:在所有bean初始化完畢,所有回調(diào)處理完成,系統(tǒng)準(zhǔn)備處理服務(wù)請(qǐng)求時(shí)觸發(fā);

(5)ApplicationFailedEvent:?jiǎn)?dòng)過程出現(xiàn)異常時(shí)候觸發(fā)。

SpringApplication.run()

如果我們使用的是SpringApplication的靜態(tài)run方法,那么,這個(gè)方法里面首先要?jiǎng)?chuàng)建一個(gè)SpringApplication對(duì)象實(shí)例,然后調(diào)用這個(gè)創(chuàng)建好的SpringApplication的實(shí)例方法。在SpringApplication實(shí)例初始化的時(shí)候,它會(huì)提前做幾件事情:

1. 根據(jù)classpath里面是否存在某個(gè)特征類(org.springframework.web.context.ConfigurableWebApplicationContext)來決定是否應(yīng)該創(chuàng)建一個(gè)為Web應(yīng)用使用的ApplicationContext類型。

2. 使用SpringFactoriesLoader在應(yīng)用的classpath中查找并加載所有可用的ApplicationContextInitializer。

3. 使用SpringFactoriesLoader在應(yīng)用的classpath中查找并加載所有可用的ApplicationListener。

4. 推斷并設(shè)置main方法的定義類。

它會(huì)執(zhí)行以下步驟:

1. 創(chuàng)建一個(gè)合適的ApplicationContext實(shí)例 (取決于classpath)。

2. 注冊(cè)一個(gè)CommandLinePropertySource,以便將命令行參數(shù)作為Spring properties。

3. 刷新application context,加載所有單例beans。

4. 激活所有CommandLineRunner beans。

Run啟動(dòng)方式

@SpringBootApplication 
public class Application {
    //方式一
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    //方式二
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(MySpringConfiguration.class);
        app.run(args);
    }
    //方式三
    public static void main(String[] args) {
        new SpringApplicationBuilder()
            .sources(Parent.class)
            .child(Application.class)
            .run(args);
    }
}

1. SpringApplication.run()的底層其實(shí)就是new了一個(gè)SpringApplication的對(duì)象,并執(zhí)行run()方法。

2. 有時(shí)我們需要?jiǎng)?chuàng)建多層次的ApplicationContext (例如,父子關(guān)系的Spring的ApplicationContext 和SpringMVC),這時(shí)我們可以使用SpringApplicationBuilder講多個(gè)方法調(diào)用串起來,通過parent() 和 child()來創(chuàng)建多層次的ApplicationContext。

啟動(dòng)信息

image

啟動(dòng)信息如下:

第 9 行,啟動(dòng)SampleController。

第10行,查找active profile,無,設(shè)為default。

第11行,刷新上下文。

第12行,初始化tomcat,設(shè)置端口8080,設(shè)置訪問方式為http。

第13行,啟動(dòng)tomcat服務(wù)。

第14行,啟動(dòng)Servlet引擎。

第15行,Spring內(nèi)嵌的WebApplicationContext 初始化開始。

第16行,Spring內(nèi)嵌的WebApplicationContext 初始化完成。

第17行,映射servlet,將 dispatcherServlet 映射到 [/] 。

第18行,映射filter,將 characterEncodingFilter 映射到 [/*] 。

第19行,映射filter,將 hiddenHttpMethodFilter 映射到 [/*] 。

第20行,映射filter,將 httpPutFormContentFilter 映射到 [/*] 。

第21行,映射filter,將 requestContextFilter 映射到 [/*] 。

第22行,查找 @ControllerAdvice。

第23行,映射路徑 "{[/]}" 到 cn.larry.spring.controller.SampleController.home()。

第24行,映射路徑 "{[/error]}" 到 org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)。

第25行,映射路徑 "{[/error],produces=[text/html]}" 到 org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)。

第30行,tomcat啟動(dòng)完畢。

第31行,SampleController啟動(dòng)耗費(fèi)的時(shí)間。

第32行,初始化 dispatcherServlet 。

第33行,dispatcherServlet 的初始化已啟動(dòng)。

第34行,dispatcherServlet 的初始化已完成。

第35行,收到shutdown關(guān)閉請(qǐng)求。

第36行,關(guān)閉AnnotationConfigEmbeddedWebApplicationContext。

SpringBootApplication的啟動(dòng)過程非常復(fù)雜,只需要在main方法里調(diào)用SpringApplicatio.run()方法即可啟動(dòng)Spring Boot應(yīng)用。

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容