Tomcat是怎樣處理Spring Boot應(yīng)用的?

近一兩年, SpringBoot 由于其減少了大量原本繁瑣的 Spring 配置,以及基于 Boot 的 SpringCloud 的推廣,越來(lái)越多的應(yīng)用開(kāi)始使用 SpringBoot進(jìn)行開(kāi)發(fā)。

而 SpringBoot 以標(biāo)準(zhǔn)Java 應(yīng)用的形式,來(lái)啟動(dòng)了一個(gè) Web 服務(wù),而將容器的存在,隱藏在一個(gè)配置文件中,使用起來(lái)很方便。而 Tomcat 就是 Spring Boot 內(nèi)置的容器之一。

這次我們來(lái)看在 SpringBoot 中, Tomcat 中怎樣被集成進(jìn)來(lái)提供服務(wù)的。

在 Spring Boot 中,實(shí)現(xiàn)也基本類(lèi)似。 區(qū)別在于配置信息大部分是默認(rèn)的,另外一些用戶特定設(shè)置的,通過(guò)在 application.properties 之類(lèi)的 Boot 配置文件里,讀出來(lái)解析并設(shè)置到 Tomcat 的各個(gè)組件上。

另外一個(gè)區(qū)別是, Spring Boot 使用的是 Embedded Tomcat 。
當(dāng)然,上面這兩點(diǎn),是整個(gè) Boot 項(xiàng)目中使用到 Tomcat 的基本原理,但具體對(duì)于 Embedded Tomcat 的使用,Boot 里和 Maven 插件的使用還是有一些區(qū)別的。

這是 Boot 使用的三個(gè)embedded 容器,默認(rèn)啟動(dòng)的是 Tomcat。

image

要分析這個(gè)問(wèn)題,該從哪看起呢?

Boot 在啟動(dòng)的時(shí)候,很清楚的告訴我們這樣一條信息

s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port

我們看到的這一條是logback輸出的信息。前面是縮略形式寫(xiě)的包名,最主要的是這個(gè)Container,跳轉(zhuǎn)到類(lèi)里看一眼。

可以匹配到這一行 log 的, 是 container 的init 方法

image

前面一些細(xì)節(jié)類(lèi)的內(nèi)容先不過(guò)多關(guān)注,進(jìn)入眼里的, 一定是這個(gè)

this.tomcat.start();

這里這個(gè) tomcat ,就是 Embedded Tomcat類(lèi)的實(shí)例。

這里 start 的操作,是將 容器啟動(dòng)起來(lái)

image

方法里的 getServer, getConnector 這些, 熟悉 Tomcat 的朋友都了解,Tomcat 內(nèi)部有以下幾個(gè)主要的組件:

  • Egine
  • Host
  • Context
  • Wrapper
  • Connector

前四個(gè)是容器從上到下的組件,是一個(gè)包含的關(guān)系。而光有這些還不足以讓我們?cè)L問(wèn)到部署的應(yīng)用,此時(shí)容器連接外界的組件 Connector 就顯的必不可少了。

而且,真正到了start 這一步的時(shí)候,容器的組件配置都已經(jīng)完成了,只是要啟動(dòng)以提供服務(wù)。 配置的這些讀取,都是在 initial 階段之前,已經(jīng)完成。

下圖是初始化階段讀取配置時(shí)的一些代碼, 沒(méi)有特別的地方,設(shè)置 BaseDir, 解析配置設(shè)置各個(gè)組件。

image

此外,在Spring Boot 應(yīng)用啟動(dòng)時(shí),會(huì)有這樣幾條日志輸出。

image

我們知道,Spring MVC 是通過(guò) DispatcherServlet 來(lái)分發(fā)處理請(qǐng)求,在 Spring Boot 出現(xiàn)之前,都是需要在web.xml里配置,來(lái)實(shí)現(xiàn)請(qǐng)求的攔截。

而在Servlet 3.0 之后,規(guī)則中新增了Dynamic Servlet、Dynamic Filter這些概念, 可以在運(yùn)行時(shí)動(dòng)態(tài)注冊(cè)組件到 Context 中。

image

所以我們觀察到的 Context 僅僅是一個(gè)空的應(yīng)用,然后再通過(guò)動(dòng)態(tài)添加Servlet、 Filter 等內(nèi)容進(jìn)去。

除了以 Jar 的形式直接執(zhí)行 Main 方法外, Spring Boot 還支持將 Boot 應(yīng)用打包成 War 文件,部署到標(biāo)準(zhǔn)和容器中,不使用 Embedded 容器。

相比執(zhí)行 Main 方法來(lái)啟動(dòng) Spring Boot 應(yīng)用,以 Web 應(yīng)用提供時(shí), Boot 的能力是如何提供的呢?

來(lái)看下面這張圖,Jar文件的META-INF中services中包含一個(gè)SCI的聲明。

image

這就是Spring Boot 在標(biāo)準(zhǔn)Web容器中能生效的秘密。

SCI是做什么的呢?

容器啟動(dòng)時(shí)會(huì)依次處理每個(gè) **ServletContainerInitializer **的HandlesTypes注解,然后分別調(diào)用所有ServletContainerInitializer對(duì)象的onStartup方法,并將處理HandlesTypes注解得到的類(lèi)數(shù)組,傳遞給ServletContainerInitializer的onStartup方法。

在configure階段,我們將 Boot 打包成 war 時(shí)提供的Initalizer,并將其 run 起來(lái)。

此時(shí)處理 dispatcherServlet 這些,和 以Main方法啟動(dòng)執(zhí)行沒(méi)什么區(qū)別。

所以,當(dāng)我們看到 Boot 應(yīng)用能夠以如此少的配置便利的作為 Web 應(yīng)用執(zhí)行時(shí),要清楚的認(rèn)識(shí)到,背后的 Embedded 容器 還是做了不少工作,同時(shí)也是和各種新的 J2EE規(guī)范有關(guān)。 而最重要的是,無(wú)論怎么變化,本質(zhì)上還是那樣,做為一個(gè)標(biāo)準(zhǔn)的 Context 在使用,區(qū)別只在于是通過(guò)解析靜態(tài)文件進(jìn)行配置,還是通過(guò)動(dòng)態(tài)添加進(jìn)行配置。

同時(shí)需要更多java相關(guān)資料以及面試心得和視頻資料的,歡迎加QQ群:810589193
免費(fèi)獲取Java工程化、高性能及分布式、高性能、高架構(gòu)、性能調(diào)優(yōu)、Spring、MyBatis、Netty源碼分析等多個(gè)知識(shí)點(diǎn)高級(jí)進(jìn)階干貨的直播免費(fèi)學(xué)習(xí)權(quán)限及相關(guān)視頻資料,還有spring和虛擬機(jī)等書(shū)籍掃描版

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容