頭禿了,二十三張圖帶你從原始碼瞭解Spring Boot 的啟動流程~
持續原創輸出,點選上方藍字關注我
目錄
前言 原始碼版本 從哪入手? 原始碼如何切分? 如何建立SpringApplication? 設定應用型別 設定初始化器(Initializer) 設定監聽器(Listener) 設定監聽器(Listener)
執行run()方法 獲取、啟動執行過程監聽器 環境構建 建立IOC容器 IOC容器的前置處理 重新整理容器 IOC容器的後置處理 發出結束執行的事件 執行Runners 總結
總結
前言
Spring Boot
專欄已經寫了五十多天了,前面二十章從基礎應用到高階整合避重就輕介紹的都是工作、面試中常見的知識點。
今天開始底層原始碼介紹的階段,相對內容比較深一點,作者也儘可能介紹的通俗易懂,層次分明一點。相信讀過我寫的Mybatis
這篇文章花了四天時間精雕細琢,力求介紹的通俗易懂,畢竟原始碼相對難度更高些,希望通過作者拆分講解能夠幫助到讀者。
如果沒讀過作者的前二十篇文章,點選前往
原始碼版本
作者Spring Boot
是基於2.4.0
。每個版本有些變化,讀者儘量和我保持一致,以防原始碼有些出入。
從哪入手?
相信很多人嘗試讀過Spring Boot
的原始碼,但是始終沒有找到合適的方法。那是因為你對Spring Boot
的各個元件、機制不是很瞭解,研究起來就像大海撈針。
至於從哪入手不是很簡單的問題嗎,當然主啟動類了,即是標註著@SpringBootApplication
main()
方法的類,如下一段程式碼:
@SpringBootApplication
public class AnnotationDemoApplication {
public static void main(String[] args) {
SpringApplication.run(AnnotationDemoApplication.class, args);
}
}
話不多說,DEBUG
伺候,別怕,搞它........
原始碼如何切分?
SpringApplication
中的靜態run()
方法並不是一步完成的,最終執行的原始碼如下:
//org.springframework.context.ConfigurableApplicationContext
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
很顯然分為兩個步驟,分別是建立SpringApplication
和執行run()
方法,下面將分為這兩個部分介紹。
如何建立SpringApplication?
建立即是new
物件了,DEBUG
跟進程式碼,最終執行的SpringApplication
構造方法如下圖:
如上圖中標註的註釋,建立過程重用的其實分為②
、③
、④
這三個階段,下面將會一一介紹每個階段做了什麼事。
設定應用型別
這個過程非常重要,直接決定了專案的型別,應用型別分為三種,都在WebApplicationType
這個列舉類中,如下:
NONE
:顧名思義,什麼都沒有,正常流程走,不額外的啟動web容器
,比如Tomcat
。SERVLET
:基於servlet
的web程式,需要啟動內嵌的servlet
web容器,比如Tomcat
。REACTIVE
:基於reactive
的web程式,需要啟動內嵌reactive
web容器,作者不是很瞭解,不便多說。
判斷的依據很簡單,就是載入對應的類,比如載入了DispatcherServlet
等則會判斷是Servlet
的web程式。原始碼如下:
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
這裡我引入了spring-boot-starter-web
,肯定是Servlet
的web程式。
設定初始化器(Initializer)
初始化器ApplicationContextInitializer
是個好東西,用於IOC
容器重新整理之前初始化一些元件,比如ServletContextApplicationContextInitializer
。
那麼如何獲取初始化器呢?跟著上圖中的程式碼進入,在SpringApplication
中的如下圖中的方法:
相對重要的就是第一步獲取初始化器的名稱了,這個肯定是全類名
了,詳細原始碼肯定在loadFactoryNames()
方法中了,跟著原始碼進入,最終呼叫的是#SpringFactoriesLoader.loadSpringFactories()
方法。
loadSpringFactories()
方法就不再詳細解釋了,其實就是從類路徑META-INF/spring.factories
中載入ApplicationContextInitializer
的值。
在spring-boot-autoconfigure
的spring.factories
檔案中的值如下圖:
上圖中的只是一部分初始化器,因為
spring.factories
檔案不止一個。
下圖中是我的demo
中注入的初始化器,現實專案中並不止這些。
這也告訴我們自定義一個
ApplicationContextInitializer
只需要實現介面,在spring.factories
檔案中設定即可。
設定監聽器(Listener)
監聽器(ApplicationListener
)這個概念在Spring
中就已經存在,主要用於監聽特定的事件(ApplicationEvent
),比如IOC容器重新整理、容器關閉等。
Spring Boot
擴充套件了ApplicationEvent
構建了SpringApplicationEvent
這個抽象類,主要用於Spring Boot
啟動過程中觸發的事件,比如程式啟動中、程式啟動完成等。如下圖:
監聽器如何獲取?從原始碼中知道其實和初始化器(ApplicationContextInitializer
)執行的是同一個方法,同樣是從META-INF/spring.factories
檔案中獲取。
在spring-boot-autoconfigure
的spring.factories
檔案中的值如下圖:
spring.factories
檔案不止一個,同樣監聽器也不止以上這些。
作者demo
中注入的一些監聽器如下圖:
總結
SpringApplication
的構建都是為了run()
方法啟動做鋪墊,構造方法中總共就有幾行程式碼,最重要的部分就是設定應用型別、設定初始化器、設定監聽器。
「注意」:初始化器和這裡的監聽器都要放置在
spring.factories
檔案中才能在這一步驟載入,否則不會生效,因此此時IOC容器
還未建立,即使將其注入到IOC容器
中也是不會生效的。
作者簡單的畫了張執行流程圖,僅供參考,如下:
執行run()方法
上面分析了SpringApplication
的構建過程,一切都做好了鋪墊,現在到了啟動的過程了。
作者根據原始碼將啟動過程分為了「8步」,下面將會一一介紹。
1. 獲取、啟動執行過程監聽器
SpringApplicationRunListener
這個監聽器和ApplicationListener
不同,它是用來監聽應用程式啟動過程的,介面的各個方法含義如下:
public interface SpringApplicationRunListener {
// 在run()方法開始執行時,該方法就立即被呼叫,可用於在初始化最早期時做一些工作
void starting();
// 當environment構建完成,ApplicationContext建立之前,該方法被呼叫
void environmentPrepared(ConfigurableEnvironment environment);
// 當ApplicationContext構建完成時,該方法被呼叫
void contextPrepared(ConfigurableApplicationContext context);
// 在ApplicationContext完成載入,但沒有被重新整理前,該方法被呼叫
void contextLoaded(ConfigurableApplicationContext context);
// 在ApplicationContext重新整理並啟動後,CommandLineRunners和ApplicationRunner未被呼叫前,該方法被呼叫
void started(ConfigurableApplicationContext context);
// 在run()方法執行完成前該方法被呼叫
void running(ConfigurableApplicationContext context);
// 當應用執行出錯時該方法被呼叫
void failed(ConfigurableApplicationContext context, Throwable exception);
}
如何獲取執行監聽器?
在SpringApplication#run()
方法中,原始碼如下:
//從spring.factories中獲取監聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
跟進getRunListeners()
方法,其實還是呼叫了loadFactoryNames()
方法從spring.factories
檔案中獲取值,如下:
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
最終注入的是EventPublishingRunListener
這個實現類,建立例項過程肯定是通過反射了,因此我們看看它的構造方法,如下圖:
這個執行監聽器內部有一個事件廣播器(SimpleApplicationEventMulticaster
),主要用來廣播特定的事件(SpringApplicationEvent
)來觸發特定的監聽器ApplicationListener
。
EventPublishingRunListener
中的每個方法用來觸發SpringApplicationEvent
中的不同子類。
如何啟動執行監聽器?
在SpringApplication#run()
方法中,原始碼如下:
//執行starting()方法
listeners.starting(bootstrapContext, this.mainApplicationClass);
執行SpringApplicationRunListeners
的starting()
方法,跟進去其實很簡單,遍歷執行上面獲取的執行監聽器,這裡只有一個EventPublishingRunListener
。因此執行的是它的starting()
方法,原始碼如下圖:
上述原始碼中邏輯很簡單,其實只是執行了multicastEvent()
方法,廣播了ApplicationStartingEvent
事件。至於multicastEvent()
內部方法感興趣的可以看看,其實就是遍歷ApplicationListener
的實現類,找到監聽ApplicationStartingEvent
這個事件的監聽器,執行onApplicationEvent()
方法。
總結
這一步其實就是廣播了ApplicationStartingEvent
事件來觸發監聽這個事件的ApplicationListener
。
因此如果自定義了
ApplicationListener
並且監聽了ApplicationStartingEvent
(應用程式開始啟動)事件,則這個監聽器將會被觸發。
2. 環境構建
這一步主要用於載入系統配置以及使用者的自定義配置(application.properties
),原始碼如下,在run()
方法中:
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
prepareEnvironment
方法內部廣播了ApplicationEnvironmentPreparedEvent
事件,原始碼如下圖:
環境構建這一步載入了系統環境配置、使用者自定義配置並且廣播了
ApplicationEnvironmentPreparedEvent
事件,觸發監聽器。
3. 建立IOC容器
原始碼在run()
方法中,如下:
context = createApplicationContext();
跟進程式碼,真正執行的是ApplicationContextFactory
方法,如下圖:
根據webApplicationType
決定建立的型別,很顯然,我這裡的是servlet
,因此建立的是AnnotationConfigServletWebServerApplicationContext
。
這一步僅僅是建立了
IOC容器
,未有其他操作。
4. IOC容器的前置處理
這一步真是精華了,在重新整理容器之前做準備,其中有一個非常關鍵的操作:將啟動類注入容器,為後續的自動化配置奠定基礎。原始碼如下:
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
prepareContext()
原始碼解析如下圖,內容還是挺多的:
從上圖可以看出步驟很多,下面將會詳細介紹幾個重點的內容。
呼叫初始化器
在 持續原創輸出,點選上方藍字關注我
目錄
前言原始碼版本從哪入手?原始碼如何切分?如何建立SpringApplication?
設定應用型別設定初始化器(Initializer)設定監聽器(Listener)設定監聽器(Listener)
執行run()方法
獲取、啟動執行過程監聽器環境構建建立IOC 持續原創輸出,點選上方藍字關注我
目錄
前言原始碼版本@SpringBootApplication幹了什麼?@EnableAutoConfiguration幹了什麼?總結
前言
為什麼Spring Boot這麼火?因為便捷,開箱即用,但是你思考過為什麼會這麼便捷嗎?傳統的SSM架構配置檔案至少要寫半天, 持續原創輸出,點選上方藍字關注我
前言
日常工作中對於Spring Boot 提供的一些啟動器可能已經足夠使用了,但是不可避免的需要自定義啟動器,比如整合一個陌生的元件,也想要達到開箱即用的效果。
在上一章節從底層原始碼介紹了Spring Boot 的自動配置的原理,未讀過的朋友建議看一下:Spring
本文承接《Android應用程式啟動詳解(一)》繼續來學習應用程式的啟動的那些事。上文提到startActivity()方法啟動一個app後經過一翻過程就到了app的入口方法ActivityThread.main()。其實我們在之前的文章中《Android的訊息機制(二)之L detail png 特點 通過 健康 進行 左右 就是 分享圖片 ??如果你有十年,20年的各類老鼻炎,鼻竇炎,中耳炎,就用麝香鼻炎靈滴鼻液。淘寶購買地址:點擊購買麝香鼻炎靈打開淘寶搜索:宜爽中醫鼻炎選用了三十多味專門治療鼻炎方面的一些中藥材,選用了珍稀名貴的野生中藥材麝 motion blog 是不是 ofo ref tms 鏈接 https 世界 拿起手機,打開電腦,指尖輕輕點那麽幾下,大千世界盡收眼前,看到這裏,各位看官大人是不是也想有一個自己的網站呢?那麽當然了,想要有一個屬於自己的網站肯定少不了存放網站文件的服務器了,接下來小編就用 轉換 AS pos run IT tree 很難 的區別 write 轉載:http://dockone.io/article/783
這篇文章希望能夠幫助讀者深入理解Docker的命令,還有容器(container)和鏡像(image)之間的區別,並深入探討容器和運行
2018年的最後一場蘋果釋出會,已經結束了。不知道看完了釋出會的小夥伴們有何感想?手都洗乾淨準備開剁了嗎?
在本次的秋季新品釋出會上,蘋果釋出了新款的 MacBook Air、iPad Pro、Apple Pencil、Mac mini。每款新品都做了較大的改變,Macbook Air 用上了
表格是由一個個單元格組成的,每一個單元格我們都可以設定,姓名和手機號碼目前是最常見的一種表格形式,有的時候我們一個個貼上複製確實非常麻煩。費時又費力。今天小編教給大家一個簡單的方法,讓大家少費功夫。
1. 開啟需要拆分的表格
2. 在B1中輸入"葛梅",選取它和下面的空行,選中空
在移動網際網路流量紅利逐漸消的今天,移動端使用者的爭奪,已從增量市場轉向存量市場,從搶佔使用者轉向搶佔使用者時間。在眾多平臺的鬥爭都進入紅海期的時候,目前尚有未完全開發的巨大流量藍海—微信小程式,隨勢而動,一發將不可收拾。 與移動網際網路市場上的其他產品進行對比,微信的存在無疑滿足了人們一項
【記坑】Iterator遍歷時,多次呼叫next(),二次遍歷需要從Collection重新獲取迭代器
2018年02月10日 11:02:46
閱讀數:681
業務需求,從一份excel表中取到X軸(專案)和Y軸(平臺)的資料,和資料庫中的資料進行比較,如果匹配不上,則把所有匹配不上的
雙十一要來了,
小哥沒來,卻來了一個陌生電話:
“王蘑菇先生,請問你是不是最近在X家網購了一套性感睡衣?我是這家網店的客服人員。您買的這套睡衣質量有問題,公司現在需要召回並退款給您。麻煩您提供一下退款賬戶……”
應該是騙子
為什麼使用Spring Cloud Zuul?
通過前幾章的介紹,我們對於Spring Cloud Netflix 下的核心元件已經瞭解了大半,利用這些元件我們已經可以構建一個簡單的微服務架構系統,比如通過使用Spring Cloud Eureka實現高可用的服務註冊中
<div class="markdown_views prism-atom-one-dark">
<!-- flowchart 箭頭圖示 勿刪 -->
本文轉自https://blog.csdn.net/xlgen157387/article/details/79006434
俗話說,站在巨人的肩膀上看世界,一般學習的時候也是先總覽一下整體,然後逐個部分個個擊破,最後形成思路,瞭解具體細節,Tomcat的結構很複雜,
一、Tomcat頂層架構
先上一張Tomcat的頂層結構圖(圖A),如下:
Tomcat中最頂層的容器是Server,代表著整個伺服器,從上圖中可以看出,一個Server可以包含至少一個Service,用於具體提供服務。
Service主要包含兩個部分:Connec
剛開始接觸Docker之後,就對容器和映象的概念有所迷惑,上一篇也簡單的說了一下之前的見解,其實並不準確,在之後的學習中再加上網上找的資料,發現了下面的這一片博文,一定要多看幾遍,看完思考再看,就會對容器和映象有更深刻的認識。
【編者的話】本文用圖文
這是一個新概念風起雲勇的時代。 就讓我們從雲端抓它幾個名詞下來,一起玩耍吧!!! “敏捷軟體開發”,“增長黑客”,“持續整合”,“DevOps”,“精益創業”,“持續交付”,“大資料”… …
OK,就這四個啦:
“敏捷軟體開發”,“持續整合”,“DevOps”,“持續交付”,
先讓我們在Wikipe
一個普通的網站發展成大型網站過程中的一種較為典型的架構演變歷程。這一步涉及的知識體系非常的多,要求對通訊、遠端呼叫、訊息機制等有深入的理解和掌握,要求的都是從理論、硬體級、作業系統級以及所採用的語言的實現都有清楚的理解。
架構演變第一步:物理分離webserver和資料庫
很久之前就有一個關於程式設計師的梗:“喲!又在寫 Bug 呢?”
據說這是對程式設計師殺傷力最大的一句話,沒有之一!之所以如此,那是因為這是句大實話啊!是的,程式設計師的人生就是Bug和Debug交織在一起的悲歌。
那!年底了,程式設計師們,來說說你今年寫過的最牛逼的bug是什麼?
相關推薦
頭禿了,二十三張圖帶你從原始碼瞭解Spring Boot 的啟動流程~
頭禿了,Spring Boot 自動配置原始碼解析瞭解一波~
頭禿了,使用@AutoConfigureBefore指定配置類順序竟沒生效?
Android應用程式啟動詳解(二)從原始碼瞭解App的啟動過程
有了麝香鼻炎靈,再也不怕得鼻炎了,二十年老鼻炎都能治好!
7張圖帶你門了解阿裏雲服務器ECS
10張圖帶你深入理解Docker容器和鏡像-轉
一張圖帶你看完 Apple 2018 秋季釋出會
再也不用貼上複製了,Ctrl+E輕鬆幫你把文字拆分到多個單元格!
國慶快到了,還在創業的你,確定不點進來了解下?
【記坑】Iterator遍歷時,多次呼叫next(),二次遍歷需要從Collection重新獲取迭代器
雙十一要來了,我去廟裡給你求了一道護身符
Spring Cloud系列(二十三) API閘道器服務Spring Cloud Zuul(Finchley.RC2版本)
四張圖帶你瞭解Tomcat系統架構--讓面試官顫抖的Tomcat回答系列!
Tomcat日記——四張圖帶你瞭解Tomcat系統架構
四張圖帶你瞭解Tomcat系統架構
學習Docker之10張圖帶你深入理解Docker容器和映象
一張圖帶你瞭解“持續交付”和“DevOps”的前世今生
10張圖帶你看懂大型網站架構演變
年底了,程式設計師來說說你今年寫過的最牛逼的bug是什麼?