從SpringBoot啟動(dòng),閱讀源碼設(shè)計(jì)
一、背景說明
初學(xué)SpringBoot框架時(shí),第一次啟動(dòng)服務(wù),直呼什么鬼?只需要簡單的幾步配置,幾個(gè)核心的注解,就可以快速實(shí)現(xiàn)工程的搭建和運(yùn)行;
雖然從Spring框架遷移到SpringBoot框架,在初期會(huì)有很多的不適應(yīng),但是更好用的框架會(huì)快速得到認(rèn)可,從而成為主流的技術(shù)選型;
對(duì)于大多數(shù)的框架或者組件來說,如果使用起來越是簡便,那么其內(nèi)部的封裝策略就越是復(fù)雜;
比如在Spring框架更新到SpringBoot版本時(shí),其用法的簡便與內(nèi)部封裝的復(fù)雜性已經(jīng)形成強(qiáng)烈的對(duì)比;再到SpringCloud微服務(wù)框架時(shí),其封裝邏輯復(fù)雜到離譜;
對(duì)于服務(wù)端的開發(fā)來說,繞不開對(duì)Spring框架的深度學(xué)習(xí),如果單純站在源碼閱讀的角度,建議先熟讀SpringBoot啟動(dòng)流程,然后再適當(dāng)擴(kuò)展其他源碼塊;
二、SpringBoot工程
首先聊一聊閱讀源碼的基本思路,從一個(gè)極簡的案例開始,圍繞案例中的核心API作為切入點(diǎn),通過對(duì)源碼邏輯的斷點(diǎn)調(diào)試,從而體會(huì)其設(shè)計(jì)的原理;
閱讀SpringBoot的源碼,可以從服務(wù)啟動(dòng)方法作為切入點(diǎn),然后不斷的分析啟動(dòng)過程涉及到的核心API和設(shè)計(jì)原理,再基于具體的啟動(dòng)日志去分析抽象的加載邏輯;
在看具體的源碼之前,還需要說下分析思路,Spring項(xiàng)目中,要注意每個(gè)API所屬工程與層級(jí),然后再去分析API之間關(guān)系,核心的構(gòu)造、屬性、方法等;
在SpringBoot的啟動(dòng)類中,有兩個(gè)核心的切入點(diǎn),一個(gè)是類的構(gòu)造方法,完成一列的初始化動(dòng)作;一個(gè)是啟動(dòng)方法,實(shí)現(xiàn)應(yīng)用上下文的創(chuàng)建和裝載;
構(gòu)造方法:
啟動(dòng)方法:
需要說明的是,由于SpringBoot服務(wù)啟動(dòng)過程涉及源碼過多,所以上面的源碼中只是羅列部分的核心切入點(diǎn),然后圍繞這些關(guān)鍵流程展開,分析一些常見的源碼設(shè)計(jì);
另外說明一點(diǎn),以下源碼的核心版本:JDK-1.8,spring-5.2.4,spring-boot-2.2.5,在不同的版本下源碼會(huì)存在差異;
三、應(yīng)用上下文
服務(wù)啟動(dòng)時(shí),根據(jù)應(yīng)用類型判斷創(chuàng)建的上下文,此處啟動(dòng)的是基于servlet的web應(yīng)用,所以也依賴相應(yīng)的web服務(wù)器,默認(rèn)為Tomcat;
啟動(dòng)方法的核心在于對(duì)應(yīng)用上下文的創(chuàng)建、準(zhǔn)備、刷新,應(yīng)用上下文是一個(gè)十分抽象的描述,可以理解為應(yīng)用運(yùn)行的整體環(huán)境,其中涉及到資源加載,配置文件裝配,運(yùn)行服務(wù)的管理等,后續(xù)的源碼分析都圍繞該API展開;
ApplicationContext:應(yīng)用上下文核心接口,在該接口中所有的方法都是只讀模式,即只能通過Get方法進(jìn)行訪問;
ConfigurableApplicationContext:上下文配置擴(kuò)展接口,提供了應(yīng)用上下文的配置能力,生命周期的維護(hù),以及在關(guān)閉之后的相關(guān)資源釋放;
AbstractApplicationContext:上下文接口抽象實(shí)現(xiàn),核心的API,對(duì)應(yīng)用上下文中的公共能力做了實(shí)現(xiàn);
ConfigurableWebApplicationContext:Web應(yīng)用上下文配置擴(kuò)展接口,提供了Web應(yīng)用的上下文配置能力;
WebServerApplicationContext:Web服務(wù)上下文,創(chuàng)建并管理Web應(yīng)用的服務(wù)器,在該流程中嵌入的是Tomcat服務(wù);
根據(jù)應(yīng)用上下文幾個(gè)核心的API設(shè)計(jì),體會(huì)Spring源碼的設(shè)計(jì)思路,從頂級(jí)的接口開始,不斷向下擴(kuò)展并且新增方法,理解抽象實(shí)現(xiàn)類的邏輯,以及服務(wù)運(yùn)行時(shí)所依賴的具體API;
四、資源加載
什么是資源,可以是各種類型的文件和配置,字節(jié)輸入流的轉(zhuǎn)換,也可以是URL資源定位,Spring框架在運(yùn)行的過程中,需要依賴Resource接口實(shí)現(xiàn)對(duì)底層資源的訪問;
Resource:資源描述的頂級(jí)接口,提供了一系列的方法,繼承InputStreamSource接口,支持將資源轉(zhuǎn)換為流的形式操作;
AbstractResource:資源訪問的抽象實(shí)現(xiàn)類,這里的設(shè)計(jì)原理與AbstractApplicationContext類似,提供資源訪問方法的基礎(chǔ)實(shí)現(xiàn);
ResourceLoader:資源加載的封裝接口,應(yīng)用下文需要依賴該接口實(shí)現(xiàn)資源的獲取與訪問;
針對(duì)不同應(yīng)用場景需求,Resource接口的實(shí)現(xiàn)類有如下幾個(gè):FileSystemResource文件系統(tǒng)資源,ClassPathResource類路徑下資源,InputStreamResource輸入流資源等;
五、應(yīng)用環(huán)境
對(duì)于Property和Environment源碼設(shè)計(jì)體系,參考上述的源碼模塊,在思路上是相似的,此處不多描述;
應(yīng)用程序的屬性和環(huán)境涉及到的參數(shù)描述非常多,比較直接的手段是通過System類中的方法輸出,至于信息如何加載,在StandardEnvironment類中提供了方法,可以斷點(diǎn)查看;
六、Bean對(duì)象
基于Spring框架的應(yīng)用程序中,由Spring容器負(fù)責(zé)創(chuàng)建,裝配,設(shè)置屬性,進(jìn)而管理整個(gè)生命周期的對(duì)象,稱為Bean對(duì)象;Bean的生命周期非常復(fù)雜,過程大致如下:實(shí)例化,屬性加載,初始化前后管理,銷毀;
BeanFactory:工廠類,Spring框架的核心能力,Bean容器的頂級(jí)接口,提供了一系列Bean對(duì)象的訪問方法,是IOC思想和依賴注入的基礎(chǔ)支撐;
ConfigurableBeanFactory:Bean容器可配置化接口,該擴(kuò)展接口只是為了允許框架內(nèi)部的即插即用和訪問bean工廠的配置方法;
AbstractBeanFactory:Bean管理的抽象實(shí)現(xiàn)類,可以查看其內(nèi)部doGetBean方法,提供Bean實(shí)例對(duì)象的獲取邏輯,如果無法獲取則執(zhí)行創(chuàng)建邏輯;
七、Tomcat服務(wù)
初次啟動(dòng)SpringBoot工程時(shí),最大的疑問就是可見Tomcat啟動(dòng)日志,但是沒有顯式的做服務(wù)器裝配,直接啟動(dòng)JAR包即可,這在流程上簡化了一大步;
WebServer:Web應(yīng)用服務(wù)器接口,比如常用的Tomcat,Jetty,Netty等,根據(jù)應(yīng)用類型選擇,只提供了啟動(dòng)、停止、獲取端口三個(gè)方法,通過WebServerApplicationContext與應(yīng)用上下文相關(guān)聯(lián);
TomcatWebServer:SpringBoot框架管理內(nèi)置Tomcat服務(wù)的核心類,對(duì)Tomcat生命周期的管理提供了一層包裝;
Tomcat:Apache組件中輕量級(jí)Tomcat啟動(dòng)器,提供了Tomcat基礎(chǔ)配置,比如默認(rèn)的Port和HostName,以及生命周期管理的方法,TomcatWebServer類中調(diào)用的就是該API中的具體方法;
八、事件模型
事件驅(qū)動(dòng)模型是復(fù)雜流程中的常用解耦手段,即通過事件發(fā)送和監(jiān)聽兩個(gè)拆解動(dòng)作,實(shí)現(xiàn)流程的分步執(zhí)行,這在SpringBoot啟動(dòng)流程和上下文裝載中更是發(fā)揮的淋漓盡致;
ApplicationEvent:應(yīng)用事件基礎(chǔ)抽象類,繼承自JDK中EventObject類,具體事件會(huì)繼承該類,內(nèi)部聲明了事件源和發(fā)生時(shí)間兩個(gè)核心屬性;
ApplicationEventMulticaster:應(yīng)用事件廣播的頂級(jí)接口,可以將指定的應(yīng)用事件廣播給適合的監(jiān)聽器;
SimpleApplicationEventMulticaster:應(yīng)用事件廣播接口的簡單實(shí)現(xiàn),可以斷點(diǎn)該類的multicastEvent方法,查看廣播時(shí)應(yīng)用事件和其相應(yīng)的監(jiān)聽器;
ApplicationListener:應(yīng)用事件監(jiān)聽器接口,繼承自JDK中EventListener接口,Spring中擴(kuò)展了多種具體的事件監(jiān)聽器,以實(shí)現(xiàn)各種不同的場景需求,比如最常見的ConfigFileApplicationListener配置文件監(jiān)聽器;
九、配置加載
SpringBoot工程中,配置文件的管理策略非常復(fù)雜,有內(nèi)部程序執(zhí)行加載配置,也有外部集成的組件配置,當(dāng)然最核心的就是工程的自定義配置;
ConfigFileApplicationListener.Loader:配置文件監(jiān)聽器的內(nèi)部類,實(shí)現(xiàn)對(duì)工程中的配置源加載,其核心邏輯在Loader.load方法中實(shí)現(xiàn),具體邏輯由相關(guān)的實(shí)現(xiàn)類完成;
PropertySourceLoader:配置加載的策略接口,在Spring工程中支持多種類型的文件配置,比如yml、yaml、properties、xml,需要通過文件的擴(kuò)展名選擇相應(yīng)的加載實(shí)現(xiàn)類;
YamlPropertySourceLoader:加載.yml或者.yaml類型的文件,SpringBoot工程中常用的配置文件類型,最終轉(zhuǎn)換成Name和Value的屬性源集合,即通過PropertySource抽象類來描述;
十、數(shù)據(jù)庫集成
Spring框架的強(qiáng)大之處還在于能夠和其他組件進(jìn)行簡單快速的集成,比如常用的數(shù)據(jù)庫、緩存、消息隊(duì)列等各種類型的組件,分析內(nèi)部的集成邏輯,會(huì)發(fā)現(xiàn)很多原理上的相似性,尤其在SpringBoot框架中,約定大于配置;
DataSourceAutoConfiguration:SpringBoot工程中數(shù)據(jù)庫的自動(dòng)化配置類,在配置中Hikari是默認(rèn)選擇的連接池,也是號(hào)稱速度最快的;
DataSourceProperties:數(shù)據(jù)源配置相關(guān)的基礎(chǔ)類,在DataSourceConfiguration配置類中,會(huì)基于參數(shù)去創(chuàng)建數(shù)據(jù)源對(duì)象;
HikariDataSource:Hikari連接池組件中的數(shù)據(jù)源API,描述數(shù)據(jù)源的具體信息,例如配置、連接池、狀態(tài)等,具體的數(shù)據(jù)庫連接邏輯是在該組件內(nèi)部完成的;
基于SpringBoot集成數(shù)據(jù)庫的原理,可以擴(kuò)展性的看看:Redis組件的RedisAutoConfiguration配置類;Kafka組件的KafkaAutoConfiguration配置類,Elasticsearch組件的RestClientAutoConfiguration配置類,在設(shè)計(jì)原理上都有異曲同工之妙;
寫在最后
從個(gè)人經(jīng)驗(yàn)來看,想要閱讀Spring框架的源碼設(shè)計(jì),需要基于應(yīng)用流程先構(gòu)建一個(gè)大的輪廓結(jié)構(gòu),理解設(shè)計(jì)中的常用策略和原理,然后再深入單個(gè)模塊的細(xì)節(jié)邏輯,這樣容易找到閱讀節(jié)奏;
本文并沒有涉及源碼中過多的細(xì)節(jié)邏輯,只是從服務(wù)啟動(dòng)作為切入點(diǎn),整理與開發(fā)關(guān)聯(lián)性較為直接的源碼模塊,描述個(gè)人對(duì)于Spring源碼閱讀的基礎(chǔ)思路。
十一、參考源碼
應(yīng)用倉庫: https://gitee.com/cicadasmile/butte-flyer-parent
組件封裝: https://gitee.com/cicadasmile/butte-frame-parent