1. 程式人生 > >Tomcat 第二篇:啟動流程

Tomcat 第二篇:啟動流程

![](https://cdn.geekdigging.com/java/tomcat/tomcat_header.jpg) ## 1 Tomcat 基本知識 首先介紹一些 Tomcat 的基本知識,防止有純小白看的雲裡霧裡的。 下面這張圖是一個下載好二進位制版的的 Tomcat ,直接解壓得到的,雖然版本是 9.x ,但是這個目錄結構和 8.5 是一致的,不影響介紹。 ![](https://cdn.geekdigging.com/java/tomcat/tomcat02/tomcat_mulu.png) - bin : 主要用來存放一些指令碼檔案,一種格式是 sh 的是在 Linux 使用的指令碼,另一種格式是 bat 的是在 Windows 中使用的指令碼。 - conf : 主要用來存放一些 Tomcat 的配置檔案,有 xml 格式的也有 properties 格式的。 - lib : 用來存放一些 Tomcat 執行時所需要的 jar 包。 - logs : 日誌目錄。 - temp : 存放一些執行過程中產生的臨時檔案。 - webapps : 這個應該很熟悉,主要用來存放應用程式,可以是 war 包或者是 jar 包。 - work : 主要用來存放 Tomcat 在執行時的編譯後文件。清空work目錄,然後重啟 Tomcat ,可以達到清除快取的作用。 現在的 `IDE(Integrated Development Environment)` 工具可以直接將 Tomcat 配置在工具中,比如最常用的 Eclipse 和 IDEA ,啟動 Tomcat 的時候只需要點一下按鈕就可以啟動,實際上這兩個 IDE 工具只是幫我們呼叫了 Tomcat 的啟動指令碼。 Tomcat 的各種指令碼都放在 bin 這個目錄下,我們開啟看一下: ![](https://cdn.geekdigging.com/java/tomcat/tomcat02/tomcat_shell.png) 可以輕易的找到兩個啟動指令碼 `startup.bat` 和 `startup.sh` 。一個是 Windows 下的指令碼,一個 Linux 下的。 同樣,還可以看到兩個停止指令碼 `shutdown.bat` 和 `shutdown.sh` 。 這是在 Tomcat 的二進位制包中,我們還可以看下原始碼包下是什麼樣的。 ![](https://cdn.geekdigging.com/java/tomcat/tomcat02/source_shell.png) 可以看到,基本上該有的都有,最重要的啟停指令碼都還在。 既然我現在是在 Windows 環境下,那麼啟動就從 `startup.bat` 看起。 ## 2 Tomcat 啟動第一步:startup.bat 第一個開啟 `startup.bat` 這個啟動指令碼,看下里面寫了啥具體,這段指令碼不長,我把裡面的內容摘出來: ```bat 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 註釋我沒摘,只摘了最後一部分會執行的內容。 第一段是在設定當前的 `CATALINA_HOME` 的環境變數,如果沒有設定的話,會將當前執行指令碼的目錄作為該環境變數的值,然後跳轉到 `:okHome` 那裡。 接下來設定了 `EXECUTABLE` 的路徑是 `%CATALINA_HOME%\bin\catalina.bat` 。 然後開始驗證驗證設定的 `EXECUTABLE` 中的 `catalina.bat` 這個檔案存不存在,如果不存在的話直接結束,如果存在的話接著往下走。 接下來是將執行指令碼前設定的其他引數儲存到 `CMD_LINE_ARGS` 這個變數中,然後在最後執行了 `catalina.bat` 這個檔案,跟了兩個引數,第一個是 `start` ,第二個就是剛才儲存的變數 `CMD_LINE_ARGS` 。 這種指令碼程式碼建議放在編輯器裡看,千萬不要使用那個 Windows 自帶的記事本,用那玩意看估計大多數人直接就看懵了。 我通常是放在 VSCode 中看,會自動對程式碼進行高亮展示,給大家看下上面這段指令碼放在 VSCode 中的樣子: ![](https://cdn.geekdigging.com/java/tomcat/tomcat02/vs_code_bat.png) 可以看到,正常註釋變灰,關鍵字變藍高亮,並且跳轉語句直接變橙色,非常方便我們閱讀。 ## 3 Tomcat 啟動第二步:catalina.bat 這個指令碼太長了,我就單純的擷取比較重要的進行介紹。 ```shell 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` 這個變數。 ```shell if not "%CATALINA_BASE%" == "" goto gotBase set "CATALINA_BASE=%CATALINA_HOME%" :gotBase ``` 接下來到了一個重點內容,判斷 `setclasspath.bat` 是否存在,如果存在的話就執行一下: ```shell 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` 的環境變數,如果沒有設定則會在控制檯列印對應的資訊。 接下來是有關於 `CLASSPATH` 的設定和校驗,然後是一堆變數的設定和校驗,無需關注,接著使用 `echo` 列印了一些資訊: ```shell 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%" ``` 各種比較重要的變數在這裡進行了一些列印,接下來又設定了一些比較重要的變數: ```shell 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` 。 這裡出現的 `_RUNJAVA` 變數,是在上面的 `setclasspath.bat` 那個指令碼中進行設定的。 而這個 `MAINCLASS` 的值是 `org.apache.catalina.startup.Bootstrap` ,這個是我們第一篇文章啟動 Tomcat 時候的那個類,不知道各位還有印象不。 接下來設定了一個變數 `ACTION` 的動作時 `start` ,用屁股想想應該是啟動的意思。 然後對引數進行了一次判斷: ```shell 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 ``` 如果沒有其他引數做覆蓋,這裡的引數就是上面設定的 `start` ,直接跳轉到最後的 `doStart` 去了,不過可以看到, Tomcat 啟動可以接受的引數有 `debug` , `run` , `start` , `stop` , `configtest` 和 `version` ,我們平時在 IDE 工具裡用的比較多的應該有 `debug` , `run` , `start` , `stop` , 剩下的兩個至少我是基本上沒有使用過,如果不是今天看到這裡了,我都不知道 Tomcat 還能有 `configtest` 和 `version` 這兩個引數。 ```shell :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` 的判斷。 接著往下看,基本上就已經到了這個指令碼的最底部,這裡執行了一句話,也是核心的一句話: ```shell %_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` ,然後是各種執行時引數和系統屬性,最後面的 `MAINCLASS` ,也就是 `org.apache.catalina.startup.Bootstrap` ,接著是 `CMD_LINE_ARGS` ,這也是一個命令引數,就是 `main` 方法上的那個 `args` 引數,最後一個引數是 `ACTION` 也就是 `start` 。 最後歸根結底就是一句話,執行 `Bootstrap` 裡面的 `main` 方法,別看這些個指令碼寫了一大堆,主要工作就是在校驗環境配置以及一些基礎系統設定和一些執行時引數的設定。 ## 4 小結 指令碼整體來講不難,而且每段指令碼上面都已經有了簡單的英文註釋,簡明扼要的寫清楚了這一段指令碼在做什麼,稍微瞭解一些基礎的 DOS 命令,都可以進行閱讀。 至於其他的指令碼可以大致瀏覽下,基本上兜兜轉轉最後還是會回到 `catalina.bat` 這個指令碼,而在這個指令碼中,最後肯定會執行 `Bootstrap` 裡面的 `main` 方法。 在 Windows 系統中,使用的是 bat 指令碼,在 Linux 系統中,就會使用 sh 指令碼,這個本質上沒有什麼大的差別,整體原理都是一樣的,有興趣的同學可以自己研究下 sh 指令碼,就當學 shell 語