JAVA中SpringBoot啟動(dòng)流程分析
一丶前言
在之前我們學(xué)習(xí)了SpringBoot自動(dòng)裝配如何實(shí)現(xiàn)的,我們總結(jié)了Spring IOC的底層原理。
但是我們還是不知道SpringApplication.run(主類.class, args)
到底做了哪些事情。本文將和大家一起看看SpringBoot啟動(dòng)的大致流程,探討SpringBoot留給我們的擴(kuò)展接口
二丶SpringBoot啟動(dòng)流程分析
上面是SpringBoot調(diào)用SpringApplication.run(主類.class, args)
啟動(dòng)的源碼,源碼并不復(fù)雜,整體流程大概如下
下面我們依據(jù)此圖,看看這些步驟SpringBoot底層源碼
1.獲取SpringApplicationRunListener
實(shí)現(xiàn)類,包裝成SpringApplicationRunListeners
SpringApplicationRunListener
是SpringBoot框架中的監(jiān)聽器,在SpringBoot啟動(dòng)到達(dá)對(duì)應(yīng)階段的時(shí)候,會(huì)回調(diào)starting
,started
等方法。
為什么SpringBoot不適應(yīng)Spring 里面的ApplicationListener
昵,因?yàn)?code>ApplicationListener依賴于Spring容器,@EventListener
注解需要EventListenerMethodProcessor
這個(gè)BeanFactoryPostProcessor
掃描,將對(duì)應(yīng)的bean和方法包裝成ApplicationListener
注冊(cè)到ApplicationContext
中(最終注冊(cè)到ApplicationEventMulticaster
事件多播器中)對(duì)于ApplicationListener
類型bean則直接走注冊(cè)到ApplicationContext
的流程,整個(gè)流程只有Spring 容器啟動(dòng)后才能進(jìn)行,如果沒(méi)有SpringApplicationRunListener
則開發(fā)者無(wú)法在SpringBoot啟動(dòng)對(duì)應(yīng)階段進(jìn)行一些擴(kuò)展邏輯的回調(diào)。
SpringApplicationRunListeners
可以看成是SpringApplicationRunListener
的門面(門面設(shè)計(jì)模式)
其使用List<SpringApplicationRunListener>
持有所有的SpringApplicationRunListener
,然后starting
等方法都是循環(huán)調(diào)用,集合中SpringApplicationRunListener
對(duì)應(yīng)的方法
SpringBoot如何獲取所有的SpringApplciationListener
這里將從META-INF/spring.factories
獲取org.springframework.boot.SpringApplicationRunListener
定義的實(shí)現(xiàn)類全限定類名,然后反射調(diào)用構(gòu)造方法(SpringApplication application, String[] args)
進(jìn)行實(shí)例化。隨后將根據(jù)@Order
或者 Ordered
接口定義的順序進(jìn)行排序,然后包裝成SpringApplicationRunListeners
注意無(wú)法使用@Component注解 標(biāo)注在SpringApplciationListener
注解上,來(lái)實(shí)現(xiàn)事件的監(jiān)聽,必須在META-INF/spring.factories
中定義,并且必須具備構(gòu)造方法(SpringApplication application, String[] args)
。
EventPublishingRunListener
SpringApplication#addListeners
允許我們注冊(cè)ApplicationListener
到SpringBoot中,然后EventPublishingRunListener
其內(nèi)部會(huì)new 一個(gè)簡(jiǎn)單的事件多播器SimpleApplicationEventMulticaster
,在對(duì)應(yīng)的SpringBoot啟動(dòng)階段,推送事件。下面式如何注冊(cè)ApplicationListener
注意這些ApplicationListener不會(huì)被注冊(cè)到Spring上下文中,意味著不會(huì)響應(yīng)Spring上下文推送的事件,除非這個(gè)ApplicationListener是一個(gè)Spring Bean 并且被Spring管理。
下圖是EventPublishingRunListener
在SpringBoot啟動(dòng)的不同階段,推送事件
2.SpringApplicationListeners#starting
沒(méi)啥好說(shuō)的,循環(huán)回調(diào)SpringApplicationRunListener#starting
方法
3.prepareEnvironment 根據(jù)項(xiàng)目選擇Environment實(shí)現(xiàn)類,并實(shí)例化
在這一步,SpringBoot會(huì)根據(jù)類路徑中的類選擇一個(gè)Environment
并實(shí)例化,并且根據(jù)當(dāng)前激活的配置,選擇對(duì)應(yīng)的配置文件,進(jìn)行解析,并保存到Environment
中。下面是SpringBoot選擇Environment的源碼
那么SpringBoot是如何判斷當(dāng)前項(xiàng)目是什么應(yīng)用類型昵?
其實(shí)根據(jù)類路徑下是否具備指定的類,然后得到指定類型,一般我們都是servlet應(yīng)用,會(huì)選擇StandardServletEnvironment
4.SpringApplicationListeners#environmentPrepared
同2.SpringApplicationListeners#starting
5.createApplicationContext
根據(jù)類路徑指定類推斷使用什么ConfigurableApplicationContext
(一般servlet應(yīng)用使用AnnotationConfigServletWebServerApplicationContext)然后實(shí)例化AnnotationConfigServletWebServerApplicationContext
AnnotationConfigServletWebServerApplicationContext#onRefresh
方法在Spring容器刷新后會(huì)被調(diào)用,這個(gè)方法將啟動(dòng)Tomcat內(nèi)嵌服務(wù)器
6.prepareContext
這個(gè)方法主要會(huì)做以下操作
回調(diào)ApplicationContextInitializer#initialize
回調(diào)所有SpringApplicationRunListener#contextPrepared
將主類包裝成BeanDefinition
,注冊(cè)到Spring容器上下文中 回調(diào)所有SpringApplicationRunListener#contextLoaded
利用SpringApplicationRunListeners
回調(diào)SpringApplicationRunListener
,同2,不在贅述
6.1從META-INFO/spring.factories中拿所有ApplicationContextInitializer
然后回調(diào)initialize方法
在spring上下文refresh方法調(diào)用前,會(huì)回調(diào)initialize
方法
這里調(diào)用前還會(huì)判斷ApplicationContextInitializer
定義的泛型,保證5這一步創(chuàng)建的上下文,符合泛型的要求
6.2 將主類包裝成BeanDefinition
,注冊(cè)到Spring容器上下文中
這一步非常重要,主類上的注解@SpringBootApplication
需要ConfigurationClassPostProcessor
解析,才能發(fā)揮@Import,@ComponentScan的作用,想要ConfigurationClassPostProcessor
處理主類的前提是主類的BeanDefinition需要在Spring容器中。
也就是說(shuō)SpringBoot的自動(dòng)裝配,和掃描包路徑下的Spring 組件的前提是,主類的BeanDefinition在Spring容器中
這里的BeanDefinitionRegistry,其實(shí)就是來(lái)自5這一步的ApplicationContext,一般來(lái)說(shuō)AnnotationConfigServletWebServerApplicationContext
內(nèi)部持有了一個(gè)DefaultListableBeanFactory
,DefaultListableBeanFactory
是BeanDefinitionRegistry
的實(shí)現(xiàn)類,其底層使用一個(gè)ConcurrentHashMap
維護(hù),key是bean的名稱,value是對(duì)應(yīng)的BeanDefinition
當(dāng)資源是一個(gè)Class
的時(shí)候,會(huì)使用AnnotatedBeanDefinitionReader
讀取Class
對(duì)象,生成BeanDefinition
這一步還支持xml的方式
7.回調(diào)SpringApplicationRunListener#contextLoaded
同2
8.刷新Spring容器上下文
《Spring源碼學(xué)習(xí)筆記12——總結(jié)篇IOC,Bean的生命周期,三大擴(kuò)展點(diǎn)》這篇博客做了詳細(xì)的分析
這里會(huì)進(jìn)行自動(dòng)裝配和包路徑掃描注冊(cè)BeanDefinition,然后實(shí)例化單例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啟動(dòng)到觸發(fā)spring容器刷新,然后觸發(fā)BeanFactory實(shí)例化所有單例,非懶加載bean的流程如下
到此這篇關(guān)于JAVA中SpringBoot啟動(dòng)流程分析的文章就介紹到這了,更多相關(guān)JAVA中SpringBoot啟動(dòng)流程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)bmp和jpeg圖片格式互轉(zhuǎn)
本文主要介紹了Java實(shí)現(xiàn)bmp和jpeg圖片格式互轉(zhuǎn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04用Java程序判斷是否是閏年的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)?lái)一篇用Java程序判斷是否是閏年的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-06-06SpringBoot配置外部靜態(tài)資源映射問(wèn)題
這篇文章主要介紹了SpringBoot配置外部靜態(tài)資源映射問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11使用SpringCache進(jìn)行緩存數(shù)據(jù)庫(kù)查詢方式
這篇文章主要介紹了使用SpringCache進(jìn)行緩存數(shù)據(jù)庫(kù)查詢方式,具有很好的參考價(jià)值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10解決maven啟動(dòng)Spring項(xiàng)目報(bào)錯(cuò)的問(wèn)題
下面小編就為大家分享一篇解決maven啟動(dòng)Spring項(xiàng)目報(bào)錯(cuò)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-12-12java擴(kuò)展Hibernate注解支持java8新時(shí)間類型
這篇文章主要介紹了java擴(kuò)展Hibernate注解支持java8新時(shí)間類型,需要的朋友可以參考下2014-04-04SpringBoot發(fā)送異步郵件流程與實(shí)現(xiàn)詳解
這篇文章主要介紹了SpringBoot發(fā)送異步郵件流程與實(shí)現(xiàn)詳解,Servlet階段郵件發(fā)送非常的復(fù)雜,如果現(xiàn)代化的Java開發(fā)是那個(gè)樣子該有多糟糕,現(xiàn)在SpringBoot中集成好了郵件發(fā)送的東西,而且操作十分簡(jiǎn)單容易上手,需要的朋友可以參考下2024-01-01Java 執(zhí)行CMD命令或執(zhí)行BAT批處理方式
這篇文章主要介紹了Java 執(zhí)行CMD命令或執(zhí)行BAT批處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08