前言
上篇文章說道Tomcat的Bootstrap類,在 start 的時候,分別調用了 Bootstrap 類的java
daemon.setAwait(true); daemon.load(args); daemon.start();
這三個方法,這三個方法都相似,都是經過反射調用 Catalina 類的同名方法,setAwait 方法比較簡單,就是把 Catalina 的 await 屬性設置爲true。本篇文章着重看一下 load 方法,和 start 方法apache
1. load 方法segmentfault
private void load(String[] arguments) throws Exception { // Call the load() method String methodName = "load"; Object param[]; Class<?> paramTypes[]; if (arguments == null || arguments.length==0) { paramTypes = null; param = null; } else { paramTypes = new Class[1]; paramTypes[0] = arguments.getClass(); param = new Object[1]; param[0] = arguments; } Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes); if (log.isDebugEnabled()) log.debug("Calling startup class " + method); method.invoke(catalinaDaemon, param); }
catalinaDaemon 就是 Catalina 對象,上面這段代碼邏輯很簡單,就是調用 catalinaDaemon 對象的 load() 或者 load(String[]) 方法。load(String[])其實也是調用 load() 方法。
1.1. Catalina#load tomcat
public void load() { if (loaded) { return; } loaded = true; long t1 = System.nanoTime(); initDirs(); // Before digester - it may be needed initNaming(); // Set configuration source ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile())); File file = configFile(); // Create and execute our Digester Digester digester = createStartDigester(); try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) { InputStream inputStream = resource.getInputStream(); InputSource inputSource = new InputSource(resource.getURI().toURL().toString()); inputSource.setByteStream(inputStream); digester.push(this); digester.parse(inputSource); } catch (Exception e) { if (file == null) { log.warn(sm.getString("catalina.configFail", getConfigFile() + "] or [server-embed.xml"), e); } else { log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e); if (file.exists() && !file.canRead()) { log.warn(sm.getString("catalina.incorrectPermissions")); } } return; } getServer().setCatalina(this); getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); // Stream redirection initStreams(); // Start the new server try { getServer().init(); } catch (LifecycleException e) { if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) { throw new java.lang.Error(e); } else { log.error(sm.getString("catalina.initError"), e); } } long t2 = System.nanoTime(); if(log.isInfoEnabled()) { log.info(sm.getString("catalina.init", Long.valueOf((t2 - t1) / 1000000))); } }
org.apache.catalina.startup.Catalina#load() 方法的邏輯挺清晰的,大概能夠分爲三段,第一個 try-catch 以前算一段,第一個 try-catch 算第二段,第一個 try-catch 到第二個 try-catch 結束算第三段。
1.1.1. 第一段 ide
先看第一段,第一段中的 initDirs() 方法和 initNaming() 方法比較簡單,能夠忽略。而後是this
ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
這一行代碼代碼的做用是設置Tomcat要加載的配置的配置源,也就是 conf 目錄下的 server.xml 文件。
而後線程
Digester digester = createStartDigester();
這一行建立了一個 Digester 對象,這個對象是用來解析 server.xml 文件的debug
/** * Create and configure the Digester we will be using for startup. * @return the main digester to parse server.xml */ protected Digester createStartDigester() { long t1=System.currentTimeMillis(); // Initialize the digester Digester digester = new Digester(); digester.setValidating(false); digester.setRulesValidation(true); …… // Configure the actions we will be using digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className"); digester.addSetProperties("Server"); digester.addSetNext("Server", "setServer", "org.apache.catalina.Server"); digester.addObjectCreate("Server/GlobalNamingResources", "org.apache.catalina.deploy.NamingResourcesImpl"); digester.addSetProperties("Server/GlobalNamingResources"); digester.addSetNext("Server/GlobalNamingResources", "setGlobalNamingResources", "org.apache.catalina.deploy.NamingResourcesImpl"); …… return digester; }
從註釋和大部分類似的代碼能夠看出,Digester 就是用來解析 server.xml 並建立對應的默認實現類對象的。好比碰到 <Server> 標籤就建立默認的 StarndardServer 類對象。關於 Digester 類就很少講了,有興趣的話能夠自行研究一下。code
1.1.2. 第二段
有了這個 Digester 對象,就能夠解析出 server.xml 裏寫的各類 xml 標籤了,第一個 try-catch 就是調用 digester.parse 方法來解析 server.xml,具體的解析過程就不往下跟了,其實就是 用 SAXParser 來解析 xml,解析完了以後,xml 裏定義的各類標籤就有對應的實現類對象了。component
1.1.3. 第三段
getServer().setCatalina(this); getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); // Stream redirection initStreams(); // Start the new server try { getServer().init(); } catch (LifecycleException e) { if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) { throw new java.lang.Error(e); } else { log.error(sm.getString("catalina.initError"), e); } }
其中 getServer() 獲取的是 Catalina 對屬性
/** * The server component we are starting or stopping. */ protected Server server = null;
這個屬性是在 digester.parse 解析 xml 的時候賦值的
這段代碼的前三行首先設置了 Server 對象的一些屬性,而後到 try-catch 裏調用 Server 對象的 init 方法。Server 對象的 init 方法涉及的東西比較多,將在下一篇文章中具體講。
2. start 方法
在 load 方法以後,Tomcat 就初始化了一系列的組件,接着就能夠調用 start 方法進行啓動了。
/** * Start a new server instance. */ public void start() { if (getServer() == null) { load(); } if (getServer() == null) { log.fatal(sm.getString("catalina.noServer")); return; } long t1 = System.nanoTime(); // Start the new server try { getServer().start(); } catch (LifecycleException e) { log.fatal(sm.getString("catalina.serverStartFail"), e); try { getServer().destroy(); } catch (LifecycleException e1) { log.debug("destroy() failed for failed Server ", e1); } return; } long t2 = System.nanoTime(); if(log.isInfoEnabled()) { log.info(sm.getString("catalina.startup", Long.valueOf((t2 - t1) / 1000000))); } // Register shutdown hook if (useShutdownHook) { if (shutdownHook == null) { shutdownHook = new CatalinaShutdownHook(); } Runtime.getRuntime().addShutdownHook(shutdownHook); // If JULI is being used, disable JULI's shutdown hook since // shutdown hooks run in parallel and log messages may be lost // if JULI's hook completes before the CatalinaShutdownHook() LogManager logManager = LogManager.getLogManager(); if (logManager instanceof ClassLoaderLogManager) { ((ClassLoaderLogManager) logManager).setUseShutdownHook( false); } } if (await) { await(); stop(); } }
上面這段代碼,邏輯很是簡單,首先肯定 getServer() 方法不爲 null ,也就是肯定 server 屬性不爲null,而 server 屬性是在 load 方法就初始化了。
整段代碼的核心就是 try-catch 裏的 getServer().start() 方法了,也就是調用 Server 對象的 start() 方法來啓動 Tomcat。本篇文章就先不對 Server 的 start() 方法進行解析了,下篇文章會單獨講。
調用完 Server#start 方法以後,註冊了一個ShutDownHook,也就是 CatalinaShutdownHook 對象,
/** * Shutdown hook which will perform a clean shutdown of Catalina if needed. */ protected class CatalinaShutdownHook extends Thread { @Override public void run() { try { if (getServer() != null) { Catalina.this.stop(); } } catch (Throwable ex) { ExceptionUtils.handleThrowable(ex); log.error(sm.getString("catalina.shutdownHookFail"), ex); } finally { // If JULI is used, shut JULI down *after* the server shuts down // so log messages aren't lost LogManager logManager = LogManager.getLogManager(); if (logManager instanceof ClassLoaderLogManager) { ((ClassLoaderLogManager) logManager).shutdown(); } } } }
CatalinaShutdownHook 的邏輯也簡單,就是調用 Catalina 對象的 stop 方法來中止 tomcat。
最後就進入 if 語句了,await 是在 Bootstrap 裏調用的時候設置爲 true 的,也就是本文開頭的時候提到的三個方法中的一個。await 方法的做用是停住主線程,等待用戶輸入shutdown 命令以後,中止等待,以後 main 線程就調用 stop 方法來中止Tomcat。
/** * Stop an existing server instance. */ public void stop() { try { // Remove the ShutdownHook first so that server.stop() // doesn't get invoked twice if (useShutdownHook) { Runtime.getRuntime().removeShutdownHook(shutdownHook); // If JULI is being used, re-enable JULI's shutdown to ensure // log messages are not lost LogManager logManager = LogManager.getLogManager(); if (logManager instanceof ClassLoaderLogManager) { ((ClassLoaderLogManager) logManager).setUseShutdownHook( true); } } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); // This will fail on JDK 1.2. Ignoring, as Tomcat can run // fine without the shutdown hook. } // Shut down the server try { Server s = getServer(); LifecycleState state = s.getState(); if (LifecycleState.STOPPING_PREP.compareTo(state) <= 0 && LifecycleState.DESTROYED.compareTo(state) >= 0) { // Nothing to do. stop() was already called } else { s.stop(); s.destroy(); } } catch (LifecycleException e) { log.error(sm.getString("catalina.stopError"), e); } }
Catalina 的 stop 方法主要邏輯是調用 Server 對象的 stop 方法。
小結Catalina 類承接了 Bootstrap 類的 load 和 start 方法,而後根據配置初始化了 Tomcat 的組件,並調用了 Server 類的 init 和 start 方法來啓動 Tomcat。