startup.bat
當我們啟動tomcat一般是運行%TOMCAT_HOME%\bin\startup.bat檔案,這個檔案實際上調用了%TOMCAT_HOME%\bin\catalina.bat批次檔:
set "EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat"......set CMD_LINE_ARGS=:setArgsif ""%1""=="""" goto doneSetArgsset CMD_LINE_ARGS=%CMD_LINE_ARGS% %1shiftgoto setArgs:doneSetArgscall "%EXECUTABLE%" start %CMD_LINE_ARGS%
startup.bat將start命令和控制台的所有參數都傳給了catalina.bat檔案,下面來看catalina.bat檔案中是怎麼啟動Tomcat的。注意到下面這句
%_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%
_EXECJAVA=_RUNJAVA="%JRE_HOME%\bin\java",這是在%TOMCAT_HOME%\bin\setclasspath.bat檔案中設定的,也就是說這句要運行一個JAVA程式,後面緊接著就應該是啟動並執行類%MAINCLASS%,MAINCLASS=org.apache.catalina.startup.Bootstrap,不錯org.apache.catalina.startup.Bootstrap類正是Tomcat的入口類,Tomcat正是從Bootstrap類的main方法開始啟動並執行。
Bootstrap
下面來看看Bootstrap的main方法做了哪些事情:
public static void main(String args[]) {if (daemon == null) {Bootstrap bootstrap = new Bootstrap();try {bootstrap.init();} catch (Throwable t) {handleThrowable(t);t.printStackTrace();return;}daemon = bootstrap;}try { ...if (command.equals("start")) daemon.start(); ...} catch (Throwable t) {...}}從以上代碼可以看出main方法做了3件事情:
1、初始化;
2、裝配tomcat容器
3、啟動tomcat容器
初始化
public void init() throws Exception { // Set Catalina path setCatalinaHome(); setCatalinaBase(); initClassLoaders(); Thread.currentThread().setContextClassLoader(catalinaLoader); SecurityClassLoad.securityClassLoad(catalinaLoader); // Load our startup class and call its process() method if (log.isDebugEnabled()) log.debug("Loading startup class"); Class<?> startupClass = catalinaLoader.loadClass ("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance(); // Set the shared extensions class loader if (log.isDebugEnabled()) log.debug("Setting startup class properties"); String methodName = "setParentClassLoader"; Class<?> paramTypes[] = new Class[1]; paramTypes[0] = Class.forName("java.lang.ClassLoader"); Object paramValues[] = new Object[1]; paramValues[0] = sharedLoader; Method method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues); catalinaDaemon = startupInstance; }1、設定catalina.home系統屬性 如果catalina.home被設定過直接返回,否則設定:如果bootstrap.jar檔案存在,就設定為這個檔案所在目錄的上一級目錄;如果不存在就設定為當前工作目錄。在%TOMCAT_HOME%\bin目錄下實際上是有這個jar檔案的,也就是catalina.home=%TOMCAT_HOME%。 2、設定catalina.base系統屬性,和1差不多 3、initClassLoaders,初始化三個classLoader: commonLoader:用於載入common/lib目錄下的jar包和class檔案
catalinaLoader:用於載入server/lib目錄下的jar包和class檔案
sharedLoader:用於載入shared/lib目錄下的jar包和class檔案 commonLoader是catalinaLoader和sharedLoader的parent classloader,Tomcat6以後,查看catalina.properties檔案可以看見server.loader和shared.loader都是為空白的。 catalina.properties檔案中common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar
4、然後設定當前線程的classloader為catalinaLoader 5、緊接著載入與安全相關的包和類 6、然後設定org.apache.catalina.startup.Catalina這個類的parent classloader為sharedLoader 7、建立org.apache.catalina.startup.Catalina這個類的執行個體並賦給catalinaDaemon
裝配容器 通過調用org.apache.catalina.startup.Catalina的load方法來實現的
public void load() {initDirs();// Before digester - it may be neededinitNaming();// Create and execute our DigesterDigester digester = createStartDigester();digester.push(this);digester.parse(inputSource);getServer().init();}}1、初始化目錄和命名規則,為digester解析XML做準備,digester準備後面再研究一下 2、建立一個digester對象,解析conf/server.xml檔案,在解析server.xml的過程中會調用StandardService.setContainer方法,將其設定為StandardEngine 3、初始化Servlet視窗,這裡的getServer()實際上返回的是StandardServer對象,這個初始化過程是在StandardServer類的initInternal方法中完成的,這個方法主要幹了兩件事,1是註冊MBean,讓JMX管理tomat容器,2是初始化Service
// Initialize our defined Services for (int i = 0; i < services.length; i++) { services[i].init(); }從代碼可以看出,一個tomcat容器是可以同時服務於多個應用的,tomcat配置多個services可以參考 http://www.ff-bb.cn/logs/109466274.html service的初始化是在StandardService類的initInternal方法中完成的
protected void initInternal() throws LifecycleException { if (container != null) { container.init(); } // Initialize any Executors for (Executor executor : findExecutors()) { if (executor instanceof LifecycleMBeanBase) { ((LifecycleMBeanBase) executor).setDomain(getDomain()); } executor.init(); } // Initialize our defined Connectors synchronized (connectors) { for (Connector connector : connectors) { connector.init(); } } } 這個方法也做了幾件事,1是初始化container,會調用StandardEngine的initInternal方法,2初始化Executor,3是初始化兩個連接器[Connector[HTTP/1.1-8080], Connector[AJP/1.3-8009]]
啟動容器 此處的start調用的是org.apache.catalina.startup的start方法
public void start() {...long t1 = System.nanoTime();// Start the new servertry {getServer().start();} catch (LifecycleException e) {log.error("Catalina.start: ", e);}...if (await) {await();stop();}} start方法會調用StandardServer、StandardService、StandardEngine、StandardHost、StandardContext、StandardWrapper的start或者startInternal方法來完成啟動過程,最後會迴圈等待,在關閉命令到來之前一直運行!
總結
1、tomcat中大量引入了模板方法模式,org.apache.catalina.util.LifecycleBase被所有容器繼承,其中initInternal和startInternal方法是抽象方法,在初始化和啟動時被每個容器會調用其init和start方法,這兩個方法會去調用對應抽象方法***Internal,這些抽象方法都是在子類中實現的
2、tomcat中大量使用了類的反射,基本上都是通過設定檔擷取類名,然後利用反射得到class對象,再通過newInstance擷取對象。