Tomcat 第二篇:啟動流程

1 Tomcat 基本知識
首先介紹一些 Tomcat 的基本知識,防止有純小白看的云里霧里的。
下面這張圖是一個下載好二進制版的的 Tomcat ,直接解壓得到的,雖然版本是 9.x ,但是這個目錄結(jié)構(gòu)和 8.5 是一致的,不影響介紹。

bin : 主要用來存放一些腳本文件,一種格式是 sh 的是在 Linux 使用的腳本,另一種格式是 bat 的是在 Windows 中使用的腳本。 conf : 主要用來存放一些 Tomcat 的配置文件,有 xml 格式的也有 properties 格式的。 lib : 用來存放一些 Tomcat 運行時所需要的 jar 包。 logs : 日志目錄。 temp : 存放一些運行過程中產(chǎn)生的臨時文件。 webapps : 這個應該很熟悉,主要用來存放應用程序,可以是 war 包或者是 jar 包。 work : 主要用來存放 Tomcat 在運行時的編譯后文件。清空work目錄,然后重啟 Tomcat ,可以達到清除緩存的作用。
現(xiàn)在的 IDE(Integrated Development Environment) 工具可以直接將 Tomcat 配置在工具中,比如最常用的 Eclipse 和 IDEA ,啟動 Tomcat 的時候只需要點一下按鈕就可以啟動,實際上這兩個 IDE 工具只是幫我們調(diào)用了 Tomcat 的啟動腳本。
Tomcat 的各種腳本都放在 bin 這個目錄下,我們打開看一下:

可以輕易的找到兩個啟動腳本 startup.bat 和 startup.sh 。一個是 Windows 下的腳本,一個 Linux 下的。
同樣,還可以看到兩個停止腳本 shutdown.bat 和 shutdown.sh 。
這是在 Tomcat 的二進制包中,我們還可以看下源碼包下是什么樣的。

可以看到,基本上該有的都有,最重要的啟停腳本都還在。
既然我現(xiàn)在是在 Windows 環(huán)境下,那么啟動就從 startup.bat 看起。
2 Tomcat 啟動第一步:startup.bat
第一個打開 startup.bat 這個啟動腳本,看下里面寫了啥具體,這段腳本不長,我把里面的內(nèi)容摘出來:
setlocal
rem Guess CATALINA_HOME if not defined
set "CURRENT_DIR=%cd%"
if not "%CATALINA_HOME%" == "" goto gotHome
set "CATALINA_HOME=%CURRENT_DIR%"
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
cd ..
set "CATALINA_HOME=%cd%"
cd "%CURRENT_DIR%"
:gotHome
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
echo The CATALINA_HOME environment variable is not defined correctly
echo This environment variable is needed to run this program
goto end
:okHome
set "EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat"
rem Check that target executable exists
if exist "%EXECUTABLE%" goto okExec
echo Cannot find "%EXECUTABLE%"
echo This file is needed to run this program
goto end
:okExec
rem Get remaining unshifted command line arguments and save them in the
set CMD_LINE_ARGS=
:setArgs
if ""%1""=="""" goto doneSetArgs
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
shift
goto setArgs
:doneSetArgs
call "%EXECUTABLE%" start %CMD_LINE_ARGS%
:end
最開始一大段的 rem 注釋我沒摘,只摘了最后一部分會執(zhí)行的內(nèi)容。
第一段是在設置當前的 CATALINA_HOME 的環(huán)境變量,如果沒有設置的話,會將當前運行腳本的目錄作為該環(huán)境變量的值,然后跳轉(zhuǎn)到 :okHome 那里。
接下來設置了 EXECUTABLE 的路徑是 %CATALINA_HOME%\bin\catalina.bat 。
然后開始驗證驗證設置的 EXECUTABLE 中的 catalina.bat 這個文件存不存在,如果不存在的話直接結(jié)束,如果存在的話接著往下走。
接下來是將運行腳本前設置的其他參數(shù)保存到 CMD_LINE_ARGS 這個變量中,然后在最后執(zhí)行了 catalina.bat 這個文件,跟了兩個參數(shù),第一個是 start ,第二個就是剛才保存的變量 CMD_LINE_ARGS 。
這種腳本代碼建議放在編輯器里看,千萬不要使用那個 Windows 自帶的記事本,用那玩意看估計大多數(shù)人直接就看懵了。
我通常是放在 VSCode 中看,會自動對代碼進行高亮展示,給大家看下上面這段腳本放在 VSCode 中的樣子:

可以看到,正常注釋變灰,關(guān)鍵字變藍高亮,并且跳轉(zhuǎn)語句直接變橙色,非常方便我們閱讀。
3 Tomcat 啟動第二步:catalina.bat
這個腳本太長了,我就單純的截取比較重要的進行介紹。
set?"CURRENT_DIR=%cd%"
if?not?"%CATALINA_HOME%"?==?""?goto?gotHome
set?"CATALINA_HOME=%CURRENT_DIR%"
if?exist?"%CATALINA_HOME%\bin\catalina.bat"?goto?okHome
cd?..
set?"CATALINA_HOME=%cd%"
cd?"%CURRENT_DIR%"
:gotHome
if?exist?"%CATALINA_HOME%\bin\catalina.bat"?goto?okHome
echo?The?CATALINA_HOME?environment?variable?is?not?defined?correctly
echo?This?environment?variable?is?needed?to?run?this?program
goto?end
:okHome
在最開頭的地方,校驗 CATALINA_HOME 是否存在,如果不存在,則設置 CATALINA_HOME 為當前路徑。
接著下面設置了一下 CATALINA_BASE 這個變量。
if?not?"%CATALINA_BASE%"?==?""?goto?gotBase
set?"CATALINA_BASE=%CATALINA_HOME%"
:gotBase
接下來到了一個重點內(nèi)容,判斷 setclasspath.bat 是否存在,如果存在的話就執(zhí)行一下:
rem?Get?standard?Java?environment?variables
if?exist?"%CATALINA_HOME%\bin\setclasspath.bat"?goto?okSetclasspath
echo?Cannot?find?"%CATALINA_HOME%\bin\setclasspath.bat"
echo?This?file?is?needed?to?run?this?program
goto?end
:okSetclasspath
call?"%CATALINA_HOME%\bin\setclasspath.bat"?%1
if?errorlevel?1?goto?end
這個文件實際上是校驗是否設置了 JAVA_HOME 或者 JRE_HOME 的環(huán)境變量,如果沒有設置則會在控制臺打印對應的信息。
接下來是有關(guān)于 CLASSPATH 的設置和校驗,然后是一堆變量的設置和校驗,無需關(guān)注,接著使用 echo 打印了一些信息:
echo?Using?CATALINA_BASE:???"%CATALINA_BASE%"
echo?Using?CATALINA_HOME:???"%CATALINA_HOME%"
echo?Using?CATALINA_TMPDIR:?"%CATALINA_TMPDIR%"
if?""%1""?==?""debug""?goto?use_jdk
echo?Using?JRE_HOME:????????"%JRE_HOME%"
goto?java_dir_displayed
:use_jdk
echo?Using?JAVA_HOME:???????"%JAVA_HOME%"
:java_dir_displayed
echo?Using?CLASSPATH:???????"%CLASSPATH%"
各種比較重要的變量在這里進行了一些打印,接下來又設置了一些比較重要的變量:
set?_EXECJAVA=%_RUNJAVA%
set?MAINCLASS=org.apache.catalina.startup.Bootstrap
set?ACTION=start
set?SECURITY_POLICY_FILE=
set?DEBUG_OPTS=
set?JPDA=
這里的 _EXECJAVA 實際上是我們在 JAVA_HOME 或者是 JRE_HOME 中 bin 下面的 java.exe 。
這里出現(xiàn)的 _RUNJAVA 變量,是在上面的 setclasspath.bat 那個腳本中進行設置的。
而這個 MAINCLASS 的值是 org.apache.catalina.startup.Bootstrap ,這個是我們第一篇文章啟動 Tomcat 時候的那個類,不知道各位還有印象不。
接下來設置了一個變量 ACTION 的動作時 start ,用屁股想想應該是啟動的意思。
然后對參數(shù)進行了一次判斷:
if?""%1""?==?""debug""?goto?doDebug
if?""%1""?==?""run""?goto?doRun
if?""%1""?==?""start""?goto?doStart
if?""%1""?==?""stop""?goto?doStop
if?""%1""?==?""configtest""?goto?doConfigTest
if?""%1""?==?""version""?goto?doVersion
如果沒有其他參數(shù)做覆蓋,這里的參數(shù)就是上面設置的 start ,直接跳轉(zhuǎn)到最后的 doStart 去了,不過可以看到, Tomcat 啟動可以接受的參數(shù)有 debug , run , start , stop , configtest 和 version ,我們平時在 IDE 工具里用的比較多的應該有 debug , run , start , stop , 剩下的兩個至少我是基本上沒有使用過,如果不是今天看到這里了,我都不知道 Tomcat 還能有 configtest 和 version 這兩個參數(shù)。
:doDebug
shift
set?_EXECJAVA=%_RUNJDB%
set?DEBUG_OPTS=-sourcepath?"%CATALINA_HOME%\..\..\java"
if?not?""%1""?==?""-security""?goto?execCmd
shift
echo?Using?Security?Manager
set?"SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"
goto?execCmd
:doRun
shift
if?not?""%1""?==?""-security""?goto?execCmd
shift
echo?Using?Security?Manager
set?"SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"
goto?execCmd
:doStart
shift
if?"%TITLE%"?==?""?set?TITLE=Tomcat
set?_EXECJAVA=start?"%TITLE%"?%_RUNJAVA%
if?not?""%1""?==?""-security""?goto?execCmd
shift
echo?Using?Security?Manager
set?"SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"
goto?execCmd
上面這一段就是我們最常用的三種啟動方式,從腳本上來看, debug 比 run 單純的多設置了兩個變量 _EXECJAVA 和 DEBUG_OPTS ,而 start 則是多了一個 TITLE 的判斷。
接著往下看,基本上就已經(jīng)到了這個腳本的最底部,這里執(zhí)行了一句話,也是核心的一句話:
%_EXECJAVA%?%CATALINA_LOGGING_CONFIG%?%LOGGING_MANAGER%?%JAVA_OPTS%?%CATALINA_OPTS%?%DEBUG_OPTS%?-D%ENDORSED_PROP%="%JAVA_ENDORSED_DIRS%"?-classpath?"%CLASSPATH%"?-Dcatalina.base="%CATALINA_BASE%"?-Dcatalina.home="%CATALINA_HOME%"?-Djava.io.tmpdir="%CATALINA_TMPDIR%"?%MAINCLASS%?%CMD_LINE_ARGS%?%ACTION%
_EXECJAVA 是我們剛才說的那個 java.exe ,然后是各種運行時參數(shù)和系統(tǒng)屬性,最后面的 MAINCLASS ,也就是 org.apache.catalina.startup.Bootstrap ,接著是 CMD_LINE_ARGS ,這也是一個命令參數(shù),就是 main 方法上的那個 args 參數(shù),最后一個參數(shù)是 ACTION 也就是 start 。
最后歸根結(jié)底就是一句話,執(zhí)行 Bootstrap 里面的 main 方法,別看這些個腳本寫了一大堆,主要工作就是在校驗環(huán)境配置以及一些基礎(chǔ)系統(tǒng)設置和一些運行時參數(shù)的設置。
4 小結(jié)
腳本整體來講不難,而且每段腳本上面都已經(jīng)有了簡單的英文注釋,簡明扼要的寫清楚了這一段腳本在做什么,稍微了解一些基礎(chǔ)的 DOS 命令,都可以進行閱讀。
至于其他的腳本可以大致瀏覽下,基本上兜兜轉(zhuǎn)轉(zhuǎn)最后還是會回到 catalina.bat 這個腳本,而在這個腳本中,最后肯定會執(zhí)行 Bootstrap 里面的 main 方法。
在 Windows 系統(tǒng)中,使用的是 bat 腳本,在 Linux 系統(tǒng)中,就會使用 sh 腳本,這個本質(zhì)上沒有什么大的差別,整體原理都是一樣的,有興趣的同學可以自己研究下 sh 腳本,就當學 shell 語法了。

