JAVA中SpringBoot啟動流程分析
一丶前言
在之前我們學習了SpringBoot自動裝配如何實現(xiàn)的,我們總結(jié)了Spring IOC的底層原理。
但是我們還是不知道SpringApplication.run(主類.class, args)到底做了哪些事情。本文將和大家一起看看SpringBoot啟動的大致流程,探討SpringBoot留給我們的擴展接口
二丶SpringBoot啟動流程分析

上面是SpringBoot調(diào)用SpringApplication.run(主類.class, args)啟動的源碼,源碼并不復雜,整體流程大概如下

下面我們依據(jù)此圖,看看這些步驟SpringBoot底層源碼
1.獲取SpringApplicationRunListener實現(xiàn)類,包裝成SpringApplicationRunListeners

SpringApplicationRunListener是SpringBoot框架中的監(jiān)聽器,在SpringBoot啟動到達對應階段的時候,會回調(diào)starting,started等方法。
為什么SpringBoot不適應Spring 里面的ApplicationListener昵,因為ApplicationListener依賴于Spring容器,@EventListener注解需要EventListenerMethodProcessor這個BeanFactoryPostProcessor掃描,將對應的bean和方法包裝成ApplicationListener注冊到ApplicationContext中(最終注冊到ApplicationEventMulticaster事件多播器中)對于ApplicationListener類型bean則直接走注冊到ApplicationContext的流程,整個流程只有Spring 容器啟動后才能進行,如果沒有SpringApplicationRunListener則開發(fā)者無法在SpringBoot啟動對應階段進行一些擴展邏輯的回調(diào)。
SpringApplicationRunListeners 可以看成是SpringApplicationRunListener的門面(門面設計模式)
其使用List<SpringApplicationRunListener>持有所有的SpringApplicationRunListener,然后starting等方法都是循環(huán)調(diào)用,集合中SpringApplicationRunListener對應的方法


SpringBoot如何獲取所有的SpringApplciationListener

這里將從META-INF/spring.factories獲取org.springframework.boot.SpringApplicationRunListener 定義的實現(xiàn)類全限定類名,然后反射調(diào)用構(gòu)造方法(SpringApplication application, String[] args)進行實例化。隨后將根據(jù)@Order 或者 Ordered接口定義的順序進行排序,然后包裝成SpringApplicationRunListeners

注意無法使用@Component注解 標注在SpringApplciationListener注解上,來實現(xiàn)事件的監(jiān)聽,必須在META-INF/spring.factories中定義,并且必須具備構(gòu)造方法(SpringApplication application, String[] args)。
EventPublishingRunListener

SpringApplication#addListeners 允許我們注冊ApplicationListener到SpringBoot中,然后EventPublishingRunListener其內(nèi)部會new 一個簡單的事件多播器SimpleApplicationEventMulticaster,在對應的SpringBoot啟動階段,推送事件。下面式如何注冊ApplicationListener

注意這些ApplicationListener不會被注冊到Spring上下文中,意味著不會響應Spring上下文推送的事件,除非這個ApplicationListener是一個Spring Bean 并且被Spring管理。
下圖是EventPublishingRunListener在SpringBoot啟動的不同階段,推送事件

2.SpringApplicationListeners#starting

沒啥好說的,循環(huán)回調(diào)SpringApplicationRunListener#starting方法
3.prepareEnvironment 根據(jù)項目選擇Environment實現(xiàn)類,并實例化
在這一步,SpringBoot會根據(jù)類路徑中的類選擇一個Environment并實例化,并且根據(jù)當前激活的配置,選擇對應的配置文件,進行解析,并保存到Environment中。下面是SpringBoot選擇Environment的源碼

那么SpringBoot是如何判斷當前項目是什么應用類型昵?
其實根據(jù)類路徑下是否具備指定的類,然后得到指定類型,一般我們都是servlet應用,會選擇StandardServletEnvironment

4.SpringApplicationListeners#environmentPrepared
同2.SpringApplicationListeners#starting
5.createApplicationContext
根據(jù)類路徑指定類推斷使用什么ConfigurableApplicationContext(一般servlet應用使用AnnotationConfigServletWebServerApplicationContext)然后實例化AnnotationConfigServletWebServerApplicationContext
AnnotationConfigServletWebServerApplicationContext#onRefresh方法在Spring容器刷新后會被調(diào)用,這個方法將啟動Tomcat內(nèi)嵌服務器
6.prepareContext
這個方法主要會做以下操作
回調(diào)ApplicationContextInitializer#initialize 回調(diào)所有SpringApplicationRunListener#contextPrepared 將主類包裝成BeanDefinition,注冊到Spring容器上下文中 回調(diào)所有SpringApplicationRunListener#contextLoaded
利用SpringApplicationRunListeners回調(diào)SpringApplicationRunListener,同2,不在贅述
6.1從META-INFO/spring.factories中拿所有ApplicationContextInitializer然后回調(diào)initialize方法

在spring上下文refresh方法調(diào)用前,會回調(diào)initialize方法

這里調(diào)用前還會判斷ApplicationContextInitializer定義的泛型,保證5這一步創(chuàng)建的上下文,符合泛型的要求
6.2 將主類包裝成BeanDefinition,注冊到Spring容器上下文中
這一步非常重要,主類上的注解@SpringBootApplication需要ConfigurationClassPostProcessor解析,才能發(fā)揮@Import,@ComponentScan的作用,想要ConfigurationClassPostProcessor處理主類的前提是主類的BeanDefinition需要在Spring容器中。
也就是說SpringBoot的自動裝配,和掃描包路徑下的Spring 組件的前提是,主類的BeanDefinition在Spring容器中

這里的BeanDefinitionRegistry,其實就是來自5這一步的ApplicationContext,一般來說AnnotationConfigServletWebServerApplicationContext內(nèi)部持有了一個DefaultListableBeanFactory,DefaultListableBeanFactory是BeanDefinitionRegistry的實現(xiàn)類,其底層使用一個ConcurrentHashMap維護,key是bean的名稱,value是對應的BeanDefinition

當資源是一個Class的時候,會使用AnnotatedBeanDefinitionReader讀取Class對象,生成BeanDefinition
這一步還支持xml的方式
7.回調(diào)SpringApplicationRunListener#contextLoaded
同2
8.刷新Spring容器上下文
《Spring源碼學習筆記12——總結(jié)篇IOC,Bean的生命周期,三大擴展點》這篇博客做了詳細的分析
這里會進行自動裝配和包路徑掃描注冊BeanDefinition,然后實例化單例bean
9.回調(diào)SpringApplicationRunListener#started
同2
10.callRunners
從spring容器中拿到ApplicationRunner,和CommandLineRunner調(diào)用run方法

三丶SpringApplication,ApplicationContext,BeanFactory 三平面
我們將SpringApplication看作是SpringBoot平面,ApplicationContext看作是Spring平面,BeanFactory看作是Bean工廠平面,SpringBoot啟動到觸發(fā)spring容器刷新,然后觸發(fā)BeanFactory實例化所有單例,非懶加載bean的流程如下

到此這篇關(guān)于JAVA中SpringBoot啟動流程分析的文章就介紹到這了,更多相關(guān)JAVA中SpringBoot啟動流程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實現(xiàn)bmp和jpeg圖片格式互轉(zhuǎn)
本文主要介紹了Java實現(xiàn)bmp和jpeg圖片格式互轉(zhuǎn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-04-04
使用SpringCache進行緩存數(shù)據(jù)庫查詢方式
這篇文章主要介紹了使用SpringCache進行緩存數(shù)據(jù)庫查詢方式,具有很好的參考價值,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
SpringBoot發(fā)送異步郵件流程與實現(xiàn)詳解
這篇文章主要介紹了SpringBoot發(fā)送異步郵件流程與實現(xiàn)詳解,Servlet階段郵件發(fā)送非常的復雜,如果現(xiàn)代化的Java開發(fā)是那個樣子該有多糟糕,現(xiàn)在SpringBoot中集成好了郵件發(fā)送的東西,而且操作十分簡單容易上手,需要的朋友可以參考下2024-01-01
Java 執(zhí)行CMD命令或執(zhí)行BAT批處理方式
這篇文章主要介紹了Java 執(zhí)行CMD命令或執(zhí)行BAT批處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08

