SpringBoot底層實現

筆者記得差不多在2015年以前,要部署一個web應用,那得準備各種web容器,比如tomcat,然後打war包,然後部署到部署到web容器的特定目錄下,以此來完成一個應用的部署,而且應用中的web。xml配置檔案是必不可少的,可是近幾年使用了springboot後,發現寫一個web應用真的太簡單了,一個SpringBootApplication註解直接搞定,什麼web。xml啥都不用了,乾淨利索。

對於SpringBoot,我想99。99%的老鐵都會使用,但是又有幾人知道為什麼加了這麼一個簡單的註解,一個web應用就這麼輕鬆的被創建出來了呢?今天筆者就來扒一扒它的神秘面紗。

先來說說SpringBoot的幾大核心能力,包括:自動裝配、內建Web容器以及整合SpringMVC,因此筆者本篇主要從上述三個維度來詳細闡述它的底層原理。

先透過一張圖來說明下SpringBoot啟動的整體流程:

SpringBoot底層實現

我們研究任何原始碼,首先得找到它的入口,SpringBoot的入口毫無疑問是SpringApplication。run方法,找到了入口,然後順藤摸瓜看看SpringBoot是如何實現上述的三大核心能力的。

1. 自動裝配

那麼什麼是自動裝配?筆者看來自動裝配就像是搭積木,將多中形狀的模型組裝在一起,對應SpringBoot中的自動裝配則是將單獨的第三方功能,組裝到Spring這個大的容器中,讓Spring可以全權管理所涉及到的Bean例項,並在整個專案中使用。

我們還是從入口入手。我們可以想下,SpringBoot肯定是需要使用到Spring的核心能力的,而Spring的核心能力就是如何管理Bean的生命週期,那就肯定脫離不了Spring的應用上下文,但是我們在使用SpringBoot的過程中,從頭到尾都沒有明確建立過Spring應用上下文。於是我們有理由相信,一定是在SpringApplication的run方法中建立了這個Spring的應用上下文,而事實上的確如此:

SpringBoot底層實現

SpringBoot底層實現

上述程式碼中,建立了AnnotationConfigServletWebServerApplicationContext,該類是SpringBoot實現的應用上下文,它是GenericApplicationContext的子類:

SpringBoot底層實現

很明顯,它具有Spring應用上下文的一切能力。在創建出了Spring應用上下文後,接下來肯定就是去掃描需要被Spring管理的類,得到BeanDefinition資訊,然後完成Bean的生命週期管理。關於Bean的生命週期管理,筆者已經寫了系列文章,感興趣的童鞋可以去看看。這裡我們只談SpringBoot是如何完成自動裝配的。

咱們順著SpringBootApplication註解,可以發現在EnableAutoConfiguration註解上有Import({AutoConfigurationImportSelector。class}的註解資訊,Spring會呼叫AutoConfigurationImportSelector的selectImports方法,將該方法返回的所有字串對應的類,走Bean的生命週期流程並進行管理:

SpringBoot底層實現

那麼這個方法返回的字串陣列就是自動裝配的玄機所在,咱們看看它的具體程式碼實現就一目瞭然了:

SpringBoot底層實現

SpringBoot底層實現

SpringBoot底層實現

簡單來說說上述程式碼:

順著getCandidateConfigurations方法看:呼叫loadSpringFactories方法,讀取所有META-INF/spring。factories目錄中的配置資訊,返回配置資訊中key為EnableAutoConfiguration型別的value值,然後篩選出非exclusions的值,就得到了將要被返回的所有字串陣列的資料。

所以一句話來回答SpringBoot是如何實現自動裝配的呢?

很簡單,Spring就是讀取專案中所有的META-INF/spring。factories配置檔案資訊,然後載入EnableAutoConfiguration對應的value值。既然Spring已經載入了這些value值到上下文容器中,那就可以使用這些value對應的Bean做為橋樑,來載入更多的其他Bean。

如果老鐵們自己實現了一些工具包,也想自動整合進來,也完全可以增加一個META-INF/spring。factories的配置檔案作為橋樑來實現,so easy,有木有?

2. 內建Web容器

上述Spring已經載入到了EnableAutoConfiguration對應的value值,在SpringBoot自己提供的spring。factories檔案中,預設支援了一堆的值,這些都是SpringBoot預設提供的自動裝配類(也可以理解為橋樑類),其中有一個名為:ServletWebServerFactoryAutoConfiguration的配置類,這個配置類中匯入了EmbeddedTomcat:

SpringBoot底層實現

而EmbeddedTomcat這個類中又透過@Bean註解配置了TomcatServletWebServerFactory:

SpringBoot底層實現

看英文就大概知道了,這個類是用來建立Tomcat的工廠類,它是ServletWebServerFactory介面的實現類:

SpringBoot底層實現

這表明在Spring應用上下文容器中已經存在了型別為ServletWebServerFactory的Bean,大家記住這個很重要,因為接下來在建立容器的時候就要用到這個Bean。

具體來看看是怎麼連結的。

在上面我們說過SpringBoot會建立一個AnnotationConfigServletWebServerApplicationContext的Spring應用上下文,Spring在執行應用上下文的refresh方法時,會執行onRefresh方法,來執行子上下文的邏輯:

SpringBoot底層實現

而這個子上下文的onRefresh方法則是執行createWebServer方法建立web服務,也就是咱們所說的Tomcat:

SpringBoot底層實現

SpringBoot底層實現

原來如此,這裡在createWebServer方法中會從Spring的Bean工廠中獲取到ServletWebServerFactory的例項,而這個例項不就是我們上面提到的TomcatServletWebServerFactory型別的例項嗎?獲取到這個ServletWebServerFactory例項後,呼叫它的getWebServer方法來建立一個web服務:

SpringBoot底層實現

沒錯,就是直接建立一個Tomcat。呵呵,大功告成!

3. 整合SpringMVC

話說,在使用SpringBoot時,寫一個Controller和在SpringMVC中方法一摸一樣,那這個又是咋做到的呢?

還是看SpringBoot自己提供的spring。factories檔案,其中有一個名為DispatcherServletAutoConfiguration的自動配置類,這個類就是那個連線SpringBoot和SpringMVC的橋樑。

我們知道,SpringMVC裡面一個核心類就是DispatcherServlet,所以我們完全可以大膽的猜想,在這個自動配置類,一定配置了DispatcherServlet,事實上也確實如此:

SpringBoot底層實現

有了這個類,一切就水到渠成?不,還沒有渠成,雖然有了這個Bean例項,但是它是如何和Tomcat容器對接上的還沒弄清楚,不是嗎?

在上述getWebServer方法中,建立好Tomcat容器後,後面會繼續執行

prepareContext方法:

SpringBoot底層實現

SpringBoot底層實現

在該方法中呼叫configureContext建立TomcatStarter,並且新增到了tomcat上下文中,而這個TomcatStarter是ServletContainerInitializer的實現類:

SpringBoot底層實現

在Servlet3。0的規範裡面明確,在web容器啟動完成後會呼叫

ServletContainerInitializer實現類的onStartup方法,於是就會進入TomcatStarter的onStartup方法:

SpringBoot底層實現

該方法中會執行所有

ServletContextInitializer型別的onStartup方法,那這個ServletContextInitializer物件是啥?我們在DispatcherServletAutoConfiguration中可以看到配置了DispatcherServletRegistrationBean例項,這個就是ServletContextInitializer的實現類:

SpringBoot底層實現

於是就會呼叫

DispatcherServletRegistrationBean的onStartup方法,從而向web容器上下文中註冊DispatcherServlet,並配置Mapping對映(預設將“/*”對映到DispatcherServlet)。

SpringBoot底層實現

SpringBoot底層實現

SpringBoot底層實現

至此,大功告成!