Tomcat源碼分析 · 壹

前言
從今天開始,我們開始分析tomcat的源碼,至于原因嘛,第一Tomcat是非常優(yōu)秀的web服務(wù)器,它占據(jù)著全球一半以上的市場份額,就連spring boot這樣的頂級框架都在用tomcat做底層實(shí)現(xiàn),這足以說明其流行程度。當(dāng)然,其流行的另一個(gè)重要原因是,它是開源的,它是apache基金會下的一個(gè)頂級項(xiàng)目,相比做java開發(fā)的小伙伴應(yīng)該沒人不知道tomcat吧。
基于以上原因,我們今天來看下Tomcat的源碼實(shí)現(xiàn)。
昨天在某乎上看到一個(gè)大佬分享了Tomcat的源碼視頻,大佬說關(guān)于源碼的學(xué)習(xí)應(yīng)該從以下幾點(diǎn)入手:
組件及功能 設(shè)計(jì)模式 線程安全 對比聯(lián)想
所以本次源碼分析我們就從以上幾點(diǎn)開始入手。我昨天說要加強(qiáng)設(shè)計(jì)模式就是從這里看來的,畢竟看清楚了設(shè)計(jì)模式,源碼分析起來就沒那么難了。
Tomcat
首先,我們看Tomcat源碼的結(jié)構(gòu):

這個(gè)結(jié)構(gòu)和我們平時(shí)下載到的發(fā)布版本基本上一致。這里源碼剖析和版本無關(guān),但是如果線上環(huán)境使用的話,建議使用最新版,因?yàn)?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">9.0.31以下的版本存在漏洞,關(guān)于漏洞加固我們昨天已經(jīng)分享過了,還沒看的小伙伴可以爬個(gè)樓看下。
我們先來看下bin下面的啟動腳本,從startup腳本中我們可以找到項(xiàng)目啟動入口,這里我們以windows環(huán)境下的bat腳本為例:


可以看到startup腳本調(diào)用的是catalina腳本,而且傳遞的參數(shù)是start:
首先從下面的腳本中我們可以推測出,這里最終應(yīng)該會通過執(zhí)行Bootstrap的main方法來啟動Tomcat,而且由于%1處的參數(shù)是start,所以最終他會去調(diào)用noJpda語句塊:

在noJpda語句塊中,由于我們的參數(shù)是start,所以他會調(diào)用doStart語句塊:

在ddStart操作中,設(shè)置了啟動參數(shù),最后調(diào)用execCme操作:

在execCmd語句塊中最終通過java命令行的方式,來運(yùn)行MAINCLASS的main方法啟動tomcat:

下面我們就來簡單看下Bootstrap的 main方法執(zhí)行過程::

首先,它以單例模式創(chuàng)建了Bootstrap實(shí)例,并執(zhí)行它的初始化操作,最后將創(chuàng)建的對象賦值給daemon對象,如果發(fā)生異常會調(diào)用handleThrowable方法進(jìn)行異常處理。這里創(chuàng)建方式是單例模式,首先daemon是一個(gè)靜態(tài)私有變量,同時(shí)它被volatile關(guān)鍵字修飾,確保它在修改后對所有線程可見,為了進(jìn)一步保證線程安全,這里還引入了daemonLock變量,并加了synchronized鎖,下面是這兩對象的定義及修飾:

在main方法的下半部分中,它需要執(zhí)行daemon的load方法和start方法來啟動容器,這里發(fā)生異常,同樣會調(diào)用handleThrowable方法來處理異常。另外從這幾行代碼中,我們可以看出來,command其實(shí)只支持通過args參數(shù)傳入的,需要注意的是,command必須是args的最后一個(gè)參數(shù):

下面我們就來逐一看下與 Bootstrap相關(guān)的幾個(gè)方法。
init
首先是init方法,這個(gè)方法主要有兩部分操作,一部分就是關(guān)于類加載器的操作,一部分就是關(guān)于Catalina的操作。

在initClassLoaders方法中,主要是創(chuàng)建了三個(gè)類加載器:

這里的common、server和shared分別表示不同的配置名,在creaeteClassLoader方法中會根據(jù)該名稱從catalina.properties文件中獲取對應(yīng)的配置

在獲取配置資源的時(shí)候,會從三個(gè)地方獲取catalina.properties文件,分別是系統(tǒng)的根目錄、conf目錄、和/org/apache/catalina/startup/包下面,但只會解析其中一個(gè),會按照我們這里說的順序解析,如果中間任意一個(gè)文件不為空,則后面的文件就不會被解析到:

在config目錄下的catalina.properties文件中,只有common.loader是有值的,其他兩個(gè)都是空的:

可以看到common.loader共配置了四個(gè)路徑,分別包括兩個(gè)目錄及其下的jar文件。
首先會在replace方法中替換其中${catalina.base}這樣配置,然后在getPaths方法中最終匹配·其中配置的內(nèi)容。

這里的gatPaths方法就是為了解析出配置文件中的路徑,然后返回:

最后,調(diào)用ClassLoaderFactory的creaeteClassLoader方法創(chuàng)建了一個(gè)URLClassLoader的實(shí)例,入?yún)⒕褪浅?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">jar文件之外的路徑:

好了,由于時(shí)間的關(guān)系,我們今天就只說init方法中的一部分,剩下的內(nèi)容明天繼續(xù),另外今天打算搞下tomcat的環(huán)境,讓tomcat能在idea環(huán)境下debug。
總結(jié)
從目前情況來看,tomcat的源碼和spring boot比起來,還是比較簡單的,當(dāng)然這也不排除正是經(jīng)歷了spring boot源碼的磨礪,才讓我們現(xiàn)在看tomcat的源碼如此地輕松。
另外有個(gè)好消息說下,由于最近一直忘記提交內(nèi)容,所以今天我專門搞了一個(gè)定時(shí)任務(wù)提交內(nèi)容,這樣以后每天六點(diǎn)定時(shí)任務(wù)會自動幫我提交內(nèi)容,我再也不用擔(dān)心寫好的內(nèi)容忘記提交了,so easy!
好了,各位小伙伴晚安吧,我要繼續(xù)搞環(huán)境了!
- END -