Tomcat启动过程源码分析一

时间:2022-07-18 16:49:18

前言

  我们都知道只需要在Tomcat中bin目录下启动startup.bat/sh,那么整个Tomcat就可以启动起来给我们提供服务,我们不免心生疑问启动startup.bat/sh以后,Tomcat到底是如何启动的,那么下面我们就来一步一步分析吧!

启动第一步(startup.bat)

  既然启动tomcat需要启动startup.bat/sh,那么我们就从startup.bat/sh开始看起吧。笔者因操作系统使用的win系统,所以就只看了startup.bat,实际上startup.sh是给linux系统准备的启动程序并且两者所做的事情完全相同,只是为不同的系统准备的而已,所以只需要查看一个即可。

  .bat文件是Windows中双击的可执行文件。为了大家能够读懂上面的程序,需要先学习一些批处理文件的基本命令。

rem:      该命令用于注释,rem起始的行不会作为代码执行
pause:    该命令用于暂停正在执行的批处理文件,并且提示用户按键,然后程序继续执行
echo:     该命令用于在dos控制台显示一段文本,相当于print,如果想要显示环境变量需要在环境变量前后加上%,例如显示操作系统 echo %OS%
echo off: 该命令可以防止将批处理文件中的具体命令打印出来,而只会输出执行结果。
@echo off:该命令与echo off相同,唯一的区别在于 @echo off不仅会隐藏具体命令还会连'echo off'这个自身命令也隐藏起来。
set:      设置环境变量,例如 set A = 100 设置A变量为100
label:    使用 ':'(冒号)来设置一个标签,供给goto命令使用,例如":init"代表一个init标签。
goto:     该命令使正在执行的命令强制跳转到他指定的标签。例如我需要跳转指定A标签下的命令,如下:goto A
not:       该命令用来取反,相当于逻辑非。
if:       该命令表示判断
exist:     该命令通常用来测试文件是否存在,一般和if一起使用
shift:     该命令用来将参数后移一位即将%2%赋值给%1%,%3%赋值给%2%,也可以理解为参数列表左移即删除现有参数列表的第一位。
call:     该命令用来调用另外一条命令。
setLocal: 该命令表示该批处理文件中修改的环境变量只在本文件中起作用,或者直到endLocal命令出现,被修改的环境变量才恢复原状。
start:    重新开启一个dos窗口。
    

  使用编辑器打开bin/startup.bat我们可以看到如下的代码:

@echo off
rem 第一部分
rem Licensed to the Apache Software Foundation (ASF) under one or more
rem contributor license agreements.  See the NOTICE file distributed with
rem this work for additional information regarding copyright ownership.
rem The ASF licenses this file to You under the Apache License, Version 2.0
rem (the "License"); you may not use this file except in compliance with
rem the License.  You may obtain a copy of the License at
rem
rem     http://www.apache.org/licenses/LICENSE-2.0
rem
rem Unless required by applicable law or agreed to in writing, software
rem distributed under the License is distributed on an "AS IS" BASIS,
rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.

rem ---------------------------------------------------------------------------
rem Start script for the CATALINA Server
rem ---------------------------------------------------------------------------

setlocal
rem 第二部分
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%"
rem 第三部分
: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
rem 第四部分
:okHome

set "EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat"

rem 第五部分
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
rem 第六部分
: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%
rem 第七部分
:end

分析

在标注第一部分的地方全是注释,对软件的License做了说明。

正式代码开始在标注第二部分的地方开始。

  1. 设置变量CURRENT_DIR为当前目录,例如你打开了tomcatbin目录,那么CURRENT_DIR就指向了bin目录。
  2. 判断系统变量CATALINA_HOME如果不是空串,那么就跳转到gotHome标签执行,实际上我们很少去设置CATALINA_HOME这个系统变量,所以我们继续往下看。
  3. 设置CATALINA_HOMECURRENT_DIR指向的目录,也就是bin目录。
  4. 判断CATALINA_HOME\bin\catalina.bat文件是否存在,如果存在就转向okHome标签,明显CATALINA_HOME现在指向的是bin目录,那么bin\bin\catalina.bat文件是不存在的,所以继续往下看,不会跳转到okHome
  5. cd..退出到上一级目录
  6. 设置CATALINA_HOME指向当前目录,也就是Tomcat的根目录。
  7. 进入CURRENT_DIR指向的目录,也就是bin

继续看标注第三部分的地方的代码。

  1. 执行gotHome标签。
  2. 如果存在CATALINA_HOME%\bin\catalina.bat这个文件就跳转执行okHome标签。

继续看标注第四部分的地方的代码。

  1. 设置变量EXECUTABLE指向catalina.bat文件

继续看标注第五部分的地方的代码。

  1. 双重保险继续判断下EXECUTABLE指向的catalina.bat文件是否存在。存在就跳转执行okExec标签。

继续看标注第六部分的地方的代码。

  1. set CMD_LINE_ARGS=表示清空变量CMD_LINE_ARGS
  2. 执行setArgs标签
  3. 第一个变量(%1%)为空字符串,那么就跳转到doneSetArgs标签,因为我们是直接运行startup.bat,没有传递任何参数,所以我们应该是跳转到doneSetArgs标签,由此也可以猜想出,如果不是使用双击执行的话,使用命令行启动startup.bat那么是可以传递参数的。
  4. 跳转到doneSetArgs,调用EXECUTABLE指向的文件,也就是catalina.bat文件,同时传递 start参数,因为CMD_LINE_ARGS为空,所以只传递了一个start参数

第七部分

  1. 可以看出end标签是很多,判断失败跳转的标签,是参数不正确的时候的结束标志。

启动第二步(catalina.bat)

  继续上面分析,在所有参数正确的条件下,startup.bat文件会设置CATALINA_HOME变量,并且调用catalina.bat文件,同时传递参数start,那么我们继续打开catalina.bat看看,这个文件又做了什么。

  catalina.bat代码如下:

@echo off
rem 第一部分
rem Suppress Terminate batch job on CTRL+C
if not ""%1"" == ""run"" goto mainEntry
if ""%TEMP%"" == """" goto mainEntry
if exist "%TEMP%\%~nx0.run" goto mainEntry
echo Y>"%TEMP%\%~nx0.run"
if not exist "%TEMP%\%~nx0.run" goto mainEntry
echo Y>"%TEMP%\%~nx0.Y"
call "%~f0" %* <"%TEMP%\%~nx0.Y"
rem Use provided errorlevel
set RETVAL=%ERRORLEVEL%
del /Q "%TEMP%\%~nx0.Y" >NUL 2>&1
exit /B %RETVAL%
:mainEntry
del /Q "%TEMP%\%~nx0.run" >NUL 2>&1

rem 第二部分
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

rem Ensure that any user defined CLASSPATH variables are not used on startup,
rem but allow them to be specified in setenv.bat, in rare case when it is needed.

rem 第三部分
set CLASSPATH=


rem Get standard environment variables
if "%CATALINA_BASE%" == "" goto gotSetenvHome
if exist "%CATALINA_BASE%\bin\setenv.bat" call "%CATALINA_BASE%\bin\setenv.bat"
goto gotSetenvBase
:gotSetenvHome
if exist "%CATALINA_HOME%\bin\setenv.bat" call "%CATALINA_HOME%\bin\setenv.bat"
:gotSetenvBase

rem 第四部分
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
set "BASEDIR=%CATALINA_HOME%"
call "%CATALINA_HOME%\bin\setclasspath.bat" %1
if errorlevel 1 goto end

rem 第五部分
rem Add on extra jar file to CLASSPATH
rem Note that there are no quotes as we do not want to introduce random
rem quotes into the CLASSPATH
if "%CLASSPATH%" == "" goto emptyClasspath
set "CLASSPATH=%CLASSPATH%;"
:emptyClasspath
set "CLASSPATH=%CLASSPATH%%CATALINA_HOME%\bin\bootstrap.jar"

if not "%CATALINA_BASE%" == "" goto gotBase
set "CATALINA_BASE=%CATALINA_HOME%"
:gotBase

if not "%CATALINA_TMPDIR%" == "" goto gotTmpdir
set "CATALINA_TMPDIR=%CATALINA_BASE%\temp"
:gotTmpdir

rem 第六部分
rem Add tomcat-juli.jar to classpath
rem tomcat-juli.jar can be over-ridden per instance
if not exist "%CATALINA_BASE%\bin\tomcat-juli.jar" goto juliClasspathHome
set "CLASSPATH=%CLASSPATH%;%CATALINA_BASE%\bin\tomcat-juli.jar"
goto juliClasspathDone
:juliClasspathHome
set "CLASSPATH=%CLASSPATH%;%CATALINA_HOME%\bin\tomcat-juli.jar"
:juliClasspathDone

if not "%LOGGING_CONFIG%" == "" goto noJuliConfig
set LOGGING_CONFIG=-Dnop
if not exist "%CATALINA_BASE%\conf\logging.properties" goto noJuliConfig
set LOGGING_CONFIG=-Djava.util.logging.config.file="%CATALINA_BASE%\conf\logging.properties"
:noJuliConfig
set JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%

if not "%LOGGING_MANAGER%" == "" goto noJuliManager
set LOGGING_MANAGER=-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
:noJuliManager
set JAVA_OPTS=%JAVA_OPTS% %LOGGING_MANAGER%


rem 第七部分
rem ----- Execute The Requested Command ---------------------------------------

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=

if not ""%1"" == ""jpda"" goto noJpda
set JPDA=jpda
if not "%JPDA_TRANSPORT%" == "" goto gotJpdaTransport
set JPDA_TRANSPORT=dt_socket
:gotJpdaTransport
if not "%JPDA_ADDRESS%" == "" goto gotJpdaAddress
set JPDA_ADDRESS=8000
:gotJpdaAddress
if not "%JPDA_SUSPEND%" == "" goto gotJpdaSuspend
set JPDA_SUSPEND=n
:gotJpdaSuspend
if not "%JPDA_OPTS%" == "" goto gotJpdaOpts
set JPDA_OPTS=-agentlib:jdwp=transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=%JPDA_SUSPEND%
:gotJpdaOpts
shift
:noJpda

rem 第八部分
if ""%1"" == ""debug"" goto doDebug
if ""%1"" == ""run"" goto doRun
if ""%1"" == ""start"" goto doStart
if ""%1"" == ""stop"" goto doStop
if ""%1"" == ""version"" goto doVersion

echo Usage:  catalina ( commands ... )
echo commands:
echo   debug             Start Catalina in a debugger
echo   debug -security   Debug Catalina with a security manager
echo   jpda start        Start Catalina under JPDA debugger
echo   run               Start Catalina in the current window
echo   run -security     Start in the current window with security manager
echo   start             Start Catalina in a separate window
echo   start -security   Start in a separate window with security manager
echo   stop              Stop Catalina
echo   version           What version of tomcat are you running?
goto end

: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 not "%OS%" == "Windows_NT" goto noTitle
if "%TITLE%" == "" set TITLE=Tomcat
set _EXECJAVA=start "%TITLE%" %_RUNJAVA%
goto gotTitle
:noTitle
set _EXECJAVA=start %_RUNJAVA%
:gotTitle
if not ""%1"" == ""-security"" goto execCmd
shift
echo Using Security Manager
set "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"
goto execCmd

:doStop
shift
set ACTION=stop
set CATALINA_OPTS=
goto execCmd

:doVersion
%_EXECJAVA% -classpath "%CATALINA_HOME%\lib\catalina.jar" org.apache.catalina.util.ServerInfo
goto end

rem 第九部分
:execCmd
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

rem Execute Java with the applicable properties
if not "%JPDA%" == "" goto doJpda
if not "%SECURITY_POLICY_FILE%" == "" goto doSecurity
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doSecurity
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doJpda
if not "%SECURITY_POLICY_FILE%" == "" goto doSecurityJpda
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %JPDA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doSecurityJpda
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %JPDA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end

:end

一眼看上去代码真的很多,也很乱,也有很多看不懂的东西,好吧,我们慢慢来分析,一步一步来。

第一部分

rem Suppress Terminate batch job on CTRL+C
if not ""%1"" == ""run"" goto mainEntry
if ""%TEMP%"" == """" goto mainEntry
if exist "%TEMP%\%~nx0.run" goto mainEntry
echo Y>"%TEMP%\%~nx0.run"
if not exist "%TEMP%\%~nx0.run" goto mainEntry
echo Y>"%TEMP%\%~nx0.Y"
call "%~f0" %* <"%TEMP%\%~nx0.Y"
rem Use provided errorlevel
set RETVAL=%ERRORLEVEL%
del /Q "%TEMP%\%~nx0.Y" >NUL 2>&1
exit /B %RETVAL%
:mainEntry
del /Q "%TEMP%\%~nx0.run" >NUL 2>&1
  1. if not ""%1"" == ""run"" goto mainEntry:第一个参数不是run,就转向mainEntry标签,我们传递的第一个参数从上面来看是start,所以直接看mainEntry标签。
  2. del /Q "%TEMP%\%~nx0.run" >NUL 2>&1,第一眼看上去什么鬼,不要方,乍一看好像是个删除的命令,但是后面的%~nx0.run >NUL 2>&1完全看不懂。遇到这种情况第一步可以先做个测试,我们新建一个test.bat文件,里面写上如下命令,看看会打印出来什么。我们如果是双击执行的话,需要在下一行加上pause来停止下窗口便于观看。

    test.bat
    @echo off
    echo 'TEMP' = %TEMP%
    echo '%~nx0.run' = %~nx0.run
    pause

可以看到如下输出

'TEMP' = C:\Users\ADMINI~1\AppData\Local\Temp 
'test.bat.run' = \test.bat.run 

由此可见 TEMP目录指向了我们环境变量的临时文件目录,%~nx0则指向了我们本身运行的文件名,不包含全目录,单纯的文件名,那么>NUL 2>&1这个东西,我们只好查阅相关资料了.

>NUL 表示前面命令执行抛出的异常将不显示

/Q 安静模式。删除全局通配符时,不要求确认

1是标准输出

2是错误输出

和 >> 都是输出重定向符号。标准输出默认是打印到控制台,如果要导入到文件,就需要使用>或>>。> 会覆盖已有的文件内容,而>>会附加到已有内容之后。

< 和 << 是输入重定向符号。从文件中读取内容。

2>&1 是把错误输出导入(合并)到标准输出流中

所以mainEntry这个标签的作用就是,删除临时目录下的catalina.bat.run文件,如果出错也不提示。第一部分的其他行代码基本大同小异,也可以继续通过echo命令来测试,第一部分的整体作用其实是针对ctrl+c命令终止进程所做的操作而已。可以参看上面的注释rem Suppress Terminate batch job on CTRL+C

第二部分

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
  1. 设置CURRENT_DIR为当前目录bin(假如你是在bin目录中启动)
  2. CATALINA_HOME不是空就转向okHome
  3. 设置CATALINA_HOME指向CURRENT_DIR的目录,也就是bin目录。
  4. 如果存在%CATALINA_HOME%\bin\catalina.bat,那么就转向okHome,否则退出到父目录,(显然不存在bin\bin\catalina.bat)
  5. 设置CATALINA_HOME为当前目录(也就是Tomcat的根目录)
  6. 进入CURRENT_HOME指向的目录,也就是bin目录,执行gotHome标签
  7. 如果存在%CATALINA_HOME%\bin\catalina.bat,就转向okHome,现在CATALINA_HOME指向的目录是Tomcat的安装目录,所以\bin\catalina.bat文件是存在的,所以转向okHome标签。
  8. 如果CATALINA_BASE变量不是空,就转向gotBase标签。(显然为空)
  9. 设置CATALINA_BASE的值等同与CATALINA_HOME的值。(现在CATALINA_BASE也指向的Tomcat的根目录了)

第三部分

set CLASSPATH=


rem Get standard environment variables
if "%CATALINA_BASE%" == "" goto gotSetenvHome
if exist "%CATALINA_BASE%\bin\setenv.bat" call "%CATALINA_BASE%\bin\setenv.bat"
goto gotSetenvBase
:gotSetenvHome
if exist "%CATALINA_HOME%\bin\setenv.bat" call "%CATALINA_HOME%\bin\setenv.bat"
:gotSetenvBase
  1. 清空CLASSPATH
  2. 如果不存在%CATALINA_BASE%\bin\setenv.bat文件,那么就转向checkSetenvHome标签,我们现在使用的是apache-tomcat-7.0.67,该文件是不存在的,所以转向checkSetenvHome标签。
  3. checkSetenvHome标签,如果存在%CATALINA_HOME%\bin\setenv.bat,那么就调用%CATALINA_HOME%\bin\setenv.bat文件,显然依旧不存在。
  4. 继续执行goto gotSetenvBasegotSetenvBase标签为空,继续往下执行。

第四部分

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
set "BASEDIR=%CATALINA_HOME%"
call "%CATALINA_HOME%\bin\setclasspath.bat" %1
if errorlevel 1 goto end
  1. 如果存在%CATALINA_HOME%\bin\setclasspath.bat,那么就转向okSetclasspath标签,(显然存在)
  2. 设置BASEDIR指向CATALINA_HOME
  3. 调用setclasspath.bat并且传递第一个参数(start),有兴趣的同学可以自行查看setclasspath.bat文件做了什么,其实就是设置了JAVA_HOME,JRE_HOME等变量的值。

第五部分

rem Add on extra jar file to CLASSPATH
rem Note that there are no quotes as we do not want to introduce random
rem quotes into the CLASSPATH
if "%CLASSPATH%" == "" goto emptyClasspath
set "CLASSPATH=%CLASSPATH%;"
:emptyClasspath
set "CLASSPATH=%CLASSPATH%%CATALINA_HOME%\bin\bootstrap.jar"

if not "%CATALINA_BASE%" == "" goto gotBase
set "CATALINA_BASE=%CATALINA_HOME%"
:gotBase

if not "%CATALINA_TMPDIR%" == "" goto gotTmpdir
set "CATALINA_TMPDIR=%CATALINA_BASE%\temp"
:gotTmpdir
  1. 如果CLASSPATH变量为空,就跳转到emptyClasspath标签,由上面能看出来,CLASSPATH变量的确是空的,所以跳转到emptyClasspath标签。
  2. 设置CLASSPATH指向%CLASSPATH%%CATALINA_HOME%\BIN\BOOTSTRAP.JAR(也就是TOMCAT安装目录下\BIN\BOOTSTRAP.JAR)。
  3. 如果CATALINA_BASE不为空(这里是为空)那么转向gotBase标签,gotBase为空标签,继续往下执行,设置CATALINA_BASE等于CATALINA_HOME
  4. 如果CATALINA_TMPDIR变量不为空(运行到这里的时候为空)转向gotTmpdir标签,否则设置CATALINA_TMPDIR的值为CATALINA_BASE\temp,也就是Tomcat安装目录下的temp文件夹。gotTmpdir为空标签。

第六部分

rem Add tomcat-juli.jar to classpath
rem tomcat-juli.jar can be over-ridden per instance
if not exist "%CATALINA_BASE%\bin\tomcat-juli.jar" goto juliClasspathHome
set "CLASSPATH=%CLASSPATH%;%CATALINA_BASE%\bin\tomcat-juli.jar"
goto juliClasspathDone
:juliClasspathHome
set "CLASSPATH=%CLASSPATH%;%CATALINA_HOME%\bin\tomcat-juli.jar"
:juliClasspathDone

if not "%LOGGING_CONFIG%" == "" goto noJuliConfig
set LOGGING_CONFIG=-Dnop
if not exist "%CATALINA_BASE%\conf\logging.properties" goto noJuliConfig
set LOGGING_CONFIG=-Djava.util.logging.config.file="%CATALINA_BASE%\conf\logging.properties"
:noJuliConfig
set JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%

if not "%LOGGING_MANAGER%" == "" goto noJuliManager
set LOGGING_MANAGER=-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
:noJuliManager
set JAVA_OPTS=%JAVA_OPTS% %LOGGING_MANAGER%
  1. 如果不存在%CATALINA_BASE%\bin\tomcat-juli.jar那么就跳转到juliClasspathHome标签,我们使用的tomcat版本是存在的,所以继续执行。
  2. 该命令set "CLASSPATH=%CLASSPATH%;%CATALINA_BASE%\bin\tomcat-juli.jar"表示将tomcat-juli.jar的路径添加到CLASSPATH中。
  3. 跳转juliClasspathDone标签,juliClasspathDone是空标签
  4. 继续执行,如果LOGGING_CONFIG变量不为空就跳转到noJuliConfig标签,否则这只LOGGING_CONFIG=-Dnop
  5. 如果不存在%CATALINA_BASE%\conf\logging.properties,那么就跳转noJuliConfig标签,否则设置LOGGING_CONFIG等于-Djava.util.logging.config.file="%CATALINA_BASE%\conf\logging.properties"
  6. noJuliConfig标签的作用就是设置JAVA_OPTS变量为JAVA_OPTS+LOGGING_CONFIG。( 这里多说一句,我们可以在catalina.bat文件的开始位置添加JAVA_OPTS变量来指定jvm的配置,具体的参数可以自行搜索。)
  7. 继续往下看,比较类似,如果LOGGING_MANAGER不为空就转向noJuliManager标签,否则设置LOGGING_MANAGER值为-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
  8. noJuliManager标签,将LOGGING_MANAGER添加到JAVA_OPTS变量。

第七部分

rem ----- Execute The Requested Command ---------------------------------------

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=

if not ""%1"" == ""jpda"" goto noJpda
set JPDA=jpda
if not "%JPDA_TRANSPORT%" == "" goto gotJpdaTransport
set JPDA_TRANSPORT=dt_socket
:gotJpdaTransport
if not "%JPDA_ADDRESS%" == "" goto gotJpdaAddress
set JPDA_ADDRESS=8000
:gotJpdaAddress
if not "%JPDA_SUSPEND%" == "" goto gotJpdaSuspend
set JPDA_SUSPEND=n
:gotJpdaSuspend
if not "%JPDA_OPTS%" == "" goto gotJpdaOpts
set JPDA_OPTS=-agentlib:jdwp=transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=%JPDA_SUSPEND%
:gotJpdaOpts
shift
:noJpda
  1. 打印相关信息,如果第一个参数等于debug,转到use_jdk标签,否则打印相关信息,转向java_dir_displayed标签,java_dir_displayed还是打印相关信息。
  2. 设置_EXECJAVA等于变量_RUNJAVA.(该变量在setclasspath.bat中设置,值为%JRE_HOME%\bin\java.exe
  3. 设置下相关变量,MAINCLASSACTION。清空SECURITY_POLICY_FILEDEBUG_OPTSJPDA
  4. 如果第一个参数不等于jpda,转向noJpda标签。noJpda为空标签,因为第一个参数是start所以,继续往下看

第八部分

if ""%1"" == ""debug"" goto doDebug
if ""%1"" == ""run"" goto doRun
if ""%1"" == ""start"" goto doStart
if ""%1"" == ""stop"" goto doStop
if ""%1"" == ""version"" goto doVersion

echo Usage:  catalina ( commands ... )
echo commands:
echo   debug             Start Catalina in a debugger
echo   debug -security   Debug Catalina with a security manager
echo   jpda start        Start Catalina under JPDA debugger
echo   run               Start Catalina in the current window
echo   run -security     Start in the current window with security manager
echo   start             Start Catalina in a separate window
echo   start -security   Start in a separate window with security manager
echo   stop              Stop Catalina
echo   version           What version of tomcat are you running?
goto end

: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 not "%OS%" == "Windows_NT" goto noTitle
if "%TITLE%" == "" set TITLE=Tomcat
set _EXECJAVA=start "%TITLE%" %_RUNJAVA%
goto gotTitle
:noTitle
set _EXECJAVA=start %_RUNJAVA%
:gotTitle
if not ""%1"" == ""-security"" goto execCmd
shift
echo Using Security Manager
set "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"
goto execCmd

:doStop
shift
set ACTION=stop
set CATALINA_OPTS=
goto execCmd

:doVersion
%_EXECJAVA% -classpath "%CATALINA_HOME%\lib\catalina.jar" org.apache.catalina.util.ServerInfo
goto end
  1. 一开始就对第一个参数进行判断,进而转向不同的标签,因为我们第一个参数是start,所以我们直接就转向了doStart标签。
  2. doStart标签,shift,参数左移,如果当前操作系统不是win,就转向noTitle标签,如果Title变量为空就将Title设置为Tomcat,设置_EXECJAVA变量为start "%TITLE%" %_RUNJAVA%
  3. 转向gotTitle标签,如果第一个参数不等于-security,转向execCmd标签。

执行到这里大概可以看出前面的都是在做准备工作,设置好各种参数,判断等等。终于要开始调用执行命令了。

第九部分

rem 第九部分
:execCmd
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

rem Execute Java with the applicable properties
if not "%JPDA%" == "" goto doJpda
if not "%SECURITY_POLICY_FILE%" == "" goto doSecurity
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doSecurity
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doJpda
if not "%SECURITY_POLICY_FILE%" == "" goto doSecurityJpda
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %JPDA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doSecurityJpda
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %JPDA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end

:end
  1. 清空CMD_LINE_ARGS变量,如果第一个变量为空,转向doneSetArgs,运行到这里,因为前面在执行doStart的时候已经执行了shift命令,所以第一个参数已经是空了,所以直接转向doneSetArgs标签。
  2. doneSetArgs为空,所以继续往下执行
  3. 如果JPDA变量不为空,转向doJpda标签,实际上我们在第七部分JPDA部分的时候基本上是跳过了jpda,所以该变量是为空的,所以直接继续执行。
  4. 如果SECURITY_POLICY_FILE变量不为空,就转向doSecurity标签,而实际上理由同上SECURITY_POLICY_FILE变量也是空的,所以继续往下执行。
  5.   %_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%

终于等到这一行命令了,参考上面所有的变量这行命令在作者的机器上可以翻译为

start "Tomcat" "D:\WorkSoftware\Java\jdk1.7.0_13\bin\java"  -Djava.util.logging.config.file="E:\cccccccccccccccccccccccc\tomcat7.0\conf\logging.properties" -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager   -Djava.endorsed.dirs="E:\cccccccccccccccccccccccc\tomcat7.0\endorsed" -classpath "E:\cccccccccccccccccccccccc\tomcat7.0\bin\bootstrap.jar;E:\cccccccccccccccccccccccc\tomcat7.0\bin\tomcat-juli.jar" -Dcatalina.base="E:\cccccccccccccccccccccccc\tomcat7.0" -Dcatalina.home="E:\cccccccccccccccccccccccc\tomcat7.0" -Djava.io.tmpdir="E:\cccccccccccccccccccccccc\tomcat7.0\temp" org.apache.catalina.startup.Bootstrap  start

这句命令的意思就是新开一个窗口,窗口名字叫做Tomcat 使用java.exe,各种参数配置balabla,最后调用org.apache.catalina.startup.Bootstrap类,(类入口当然是main方法)传递参数为start!!!

总结:

  1. tomcat启动入口是startup.bat文件,该文件会设置CATALINA_HOME,CATALINA_BASE等常见变量,并且调用catalina.bat文件,传递start参数。
  2. catalina.bat文件,所做的事情就是检查关键文件是否存在,设置关键变量,例如CATALINA_HOME,CATALINA_BASE,classpath等等,最后新开一个窗口调用Bootstrap类的main方法,传递参数start,并且将一大堆配置传递过去。

所以我们想继续探寻,就需要去Tomcat源码中找寻 Bootstrap类,继续查看相关方法!