在前一章 查看tomcat啟動文件都干點啥---Bootstrap.java 中我們得出結(jié)論,在Bootstrap中通過反射調(diào)用Catalina類中的getServer,start,stop,stopServer等方法,下面看一下Catalina類中給外部提供的公共方法:
Start :其中Catalina類的入口當然是start方法.start方法實現(xiàn)了啟動一個新的server事例的功能,看一下start方法的內(nèi)容:

1 public void start() { 2 3 if (getServer() == null ) { 4 load(); 5 } 6 7 if (getServer() == null ) { 8 log.fatal("Cannot start server. Server instance is not configured." ); 9 return ; 10 } 11 12 long t1 = System.nanoTime(); 13 14 // Start the new server 15 try { 16 getServer().start(); 17 } catch (LifecycleException e) { 18 log.fatal(sm.getString("catalina.serverStartFail" ), e); 19 try { 20 getServer().destroy(); 21 } catch (LifecycleException e1) { 22 log.debug("destroy() failed for failed Server " , e1); 23 } 24 return ; 25 } 26 27 long t2 = System.nanoTime(); 28 if (log.isInfoEnabled()) { 29 log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms" ); 30 } 31 32 // Register shutdown hook 33 if (useShutdownHook) { 34 if (shutdownHook == null ) { 35 shutdownHook = new CatalinaShutdownHook(); 36 } 37 Runtime.getRuntime().addShutdownHook(shutdownHook); 38 39 // If JULI is being used, disable JULI's shutdown hook since 40 // shutdown hooks run in parallel and log messages may be lost 41 // if JULI's hook completes before the CatalinaShutdownHook() 42 LogManager logManager = LogManager.getLogManager(); 43 if (logManager instanceof ClassLoaderLogManager) { 44 ((ClassLoaderLogManager) logManager).setUseShutdownHook( 45 false ); 46 } 47 } 48 49 if (await) { 50 await(); 51 stop(); 52 } 53 }
在Catalina中有個很重要的對象就是Server,先說明一下,在tomcat中實現(xiàn)Server接口的StandardServer對象,其中定義了socketServer,在此只作此說明,不展開介紹,在下一章中會專門對StandardServer類以及Server接口進行說明。
在start方法中首先需要判斷是否初始化了實現(xiàn)server接口的類(以后都稱作server類,不要誤解Server為一個類),如果沒有的話,那么調(diào)用load方法。
load方法中調(diào)用了一下幾個方法:
initDirs:將Bootstrap中定義的catalina.home的值賦給CATALINA_BASE_PROP屬性。以及對java.io.tmpdir屬性的驗證,下面是initDirs的代碼實現(xiàn):

1 protected void initDirs() { 2 3 String catalinaHome = System.getProperty(Globals.CATALINA_HOME_PROP); 4 if (catalinaHome == null ) { 5 // Backwards compatibility patch for J2EE RI 1.3 6 String j2eeHome = System.getProperty("com.sun.enterprise.home" ); 7 if (j2eeHome != null ) { 8 catalinaHome=System.getProperty("com.sun.enterprise.home" ); 9 } else if (System.getProperty(Globals.CATALINA_BASE_PROP) != null ) { 10 catalinaHome = System.getProperty(Globals.CATALINA_BASE_PROP); 11 } 12 } 13 // last resort - for minimal/embedded cases. 14 if (catalinaHome== null ) { 15 catalinaHome=System.getProperty("user.dir" ); 16 } 17 if (catalinaHome != null ) { 18 File home = new File(catalinaHome); 19 if (! home.isAbsolute()) { 20 try { 21 catalinaHome = home.getCanonicalPath(); 22 } catch (IOException e) { 23 catalinaHome = home.getAbsolutePath(); 24 } 25 } 26 System.setProperty(Globals.CATALINA_HOME_PROP, catalinaHome); 27 } 28 29 if (System.getProperty(Globals.CATALINA_BASE_PROP) == null ) { 30 System.setProperty(Globals.CATALINA_BASE_PROP, 31 catalinaHome); 32 } else { 33 String catalinaBase = System.getProperty(Globals.CATALINA_BASE_PROP); 34 File base = new File(catalinaBase); 35 if (! base.isAbsolute()) { 36 try { 37 catalinaBase = base.getCanonicalPath(); 38 } catch (IOException e) { 39 catalinaBase = base.getAbsolutePath(); 40 } 41 } 42 System.setProperty(Globals.CATALINA_BASE_PROP, catalinaBase); 43 } 44 45 String temp = System.getProperty("java.io.tmpdir" ); 46 if (temp == null || (!( new File(temp)).exists()) 47 || (!( new File(temp)).isDirectory())) { 48 log.error(sm.getString("embedded.notmp" , temp)); 49 } 50 51 }
其中首先是兼容J2EE RI 1.3,獲取com.sun.enterprise.home屬性的值賦值給catalinaHome,如果不存在com.sun.enterprise.home這個屬性,將Bootstrap中定義的catalina.home的值賦給CATALINA_BASE_PROP屬性,如果以上都不成立,那么就是獲取當前目錄賦給CATALINA_BASE_PROP屬性。其實當前目錄也就是將Bootstrap中定義的catalina.home的值。只是在tomcat中進行了很繁瑣的驗證,當然這是有必要的。
createStartDigester:用來生成server.xml的操作,下面是代碼實現(xiàn):

1 protected Digester createStartDigester() { 2 long t1= System.currentTimeMillis(); 3 // Initialize the digester 4 Digester digester = new Digester(); 5 digester.setValidating( false ); 6 digester.setRulesValidation( true ); 7 HashMap<Class<?>, List<String>> fakeAttributes = 8 new HashMap<Class<?>, List<String>> (); 9 ArrayList<String> attrs = new ArrayList<String> (); 10 attrs.add("className" ); 11 fakeAttributes.put(Object. class , attrs); 12 digester.setFakeAttributes(fakeAttributes); 13 digester.setUseContextClassLoader( true ); 14 15 // Configure the actions we will be using 16 digester.addObjectCreate("Server" , 17 "org.apache.catalina.core.StandardServer" , 18 "className" ); 19 digester.addSetProperties("Server" ); 20 digester.addSetNext("Server" , 21 "setServer" , 22 "org.apache.catalina.Server" ); 23 24 digester.addObjectCreate("Server/GlobalNamingResources" , 25 "org.apache.catalina.deploy.NamingResources" ); 26 digester.addSetProperties("Server/GlobalNamingResources" ); 27 digester.addSetNext("Server/GlobalNamingResources" , 28 "setGlobalNamingResources" , 29 "org.apache.catalina.deploy.NamingResources" ); 30 31 digester.addObjectCreate("Server/Listener" , 32 null , // MUST be specified in the element 33 "className" ); 34 digester.addSetProperties("Server/Listener" ); 35 digester.addSetNext("Server/Listener" , 36 "addLifecycleListener" , 37 "org.apache.catalina.LifecycleListener" ); 38 39 digester.addObjectCreate("Server/Service" , 40 "org.apache.catalina.core.StandardService" , 41 "className" ); 42 digester.addSetProperties("Server/Service" ); 43 digester.addSetNext("Server/Service" , 44 "addService" , 45 "org.apache.catalina.Service" ); 46 47 digester.addObjectCreate("Server/Service/Listener" , 48 null , // MUST be specified in the element 49 "className" ); 50 digester.addSetProperties("Server/Service/Listener" ); 51 digester.addSetNext("Server/Service/Listener" , 52 "addLifecycleListener" , 53 "org.apache.catalina.LifecycleListener" ); 54 55 // Executor 56 digester.addObjectCreate("Server/Service/Executor" , 57 "org.apache.catalina.core.StandardThreadExecutor" , 58 "className" ); 59 digester.addSetProperties("Server/Service/Executor" ); 60 61 digester.addSetNext("Server/Service/Executor" , 62 "addExecutor" , 63 "org.apache.catalina.Executor" ); 64 65 66 digester.addRule("Server/Service/Connector" , 67 new ConnectorCreateRule()); 68 digester.addRule("Server/Service/Connector" , 69 new SetAllPropertiesRule( new String[]{"executor" })); 70 digester.addSetNext("Server/Service/Connector" , 71 "addConnector" , 72 "org.apache.catalina.connector.Connector" ); 73 74 75 digester.addObjectCreate("Server/Service/Connector/Listener" , 76 null , // MUST be specified in the element 77 "className" ); 78 digester.addSetProperties("Server/Service/Connector/Listener" ); 79 digester.addSetNext("Server/Service/Connector/Listener" , 80 "addLifecycleListener" , 81 "org.apache.catalina.LifecycleListener" ); 82 83 // Add RuleSets for nested elements 84 digester.addRuleSet( new NamingRuleSet("Server/GlobalNamingResources/" )); 85 digester.addRuleSet( new EngineRuleSet("Server/Service/" )); 86 digester.addRuleSet( new HostRuleSet("Server/Service/Engine/" )); 87 digester.addRuleSet( new ContextRuleSet("Server/Service/Engine/Host/" )); 88 addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/" ); 89 digester.addRuleSet( new NamingRuleSet("Server/Service/Engine/Host/Context/" )); 90 91 // When the 'engine' is found, set the parentClassLoader. 92 digester.addRule("Server/Service/Engine" , 93 new SetParentClassLoaderRule(parentClassLoader)); 94 addClusterRuleSet(digester, "Server/Service/Engine/Cluster/" ); 95 96 long t2= System.currentTimeMillis(); 97 if (log.isDebugEnabled()) { 98 log.debug("Digester for server.xml created " + ( t2- t1 )); 99 } 100 return (digester); 101 102 }
在具體說明之前,我覺得有必要對Digester進行一下說明,以為可能有很多人和我一樣,目前為止還還不是很清楚Digester為什么東西,其實他就是一個XML解析器,在這里就是構(gòu)造一下tomcat啟動時候的各種參數(shù),各種初始化方法,初始化server,listener,connector,Executor等數(shù)據(jù),我覺得這里有很多內(nèi)容可以展開來說,所以我打算把他放到下一個章節(jié)專門對tomcat中Digester進行說明。在這里特別需要注意的就是如下這部分內(nèi)容:
1 digester.addObjectCreate("Server" , 2 "org.apache.catalina.core.StandardServer" , 3 "className" ); 4 digester.addSetProperties("Server" ); 5 digester.addSetNext("Server" , 6 "setServer" , 7 "org.apache.catalina.Server" ); 8 9 digester.addObjectCreate("Server/GlobalNamingResources" , 10 "org.apache.catalina.deploy.NamingResources" ); 11 digester.addSetProperties("Server/GlobalNamingResources" ); 12 digester.addSetNext("Server/GlobalNamingResources" , 13 "setGlobalNamingResources" , 14 "org.apache.catalina.deploy.NamingResources" ); 15 16 digester.addObjectCreate("Server/Listener" , 17 null , // MUST be specified in the element 18 "className" ); 19 digester.addSetProperties("Server/Listener" ); 20 digester.addSetNext("Server/Listener" , 21 "addLifecycleListener" , 22 "org.apache.catalina.LifecycleListener" ); 23 24 digester.addObjectCreate("Server/Service" , 25 "org.apache.catalina.core.StandardService" , 26 "className" ); 27 digester.addSetProperties("Server/Service" ); 28 digester.addSetNext("Server/Service" , 29 "addService" , 30 "org.apache.catalina.Service" ); 31 32 digester.addObjectCreate("Server/Service/Listener" , 33 null , // MUST be specified in the element 34 "className" ); 35 digester.addSetProperties("Server/Service/Listener" ); 36 digester.addSetNext("Server/Service/Listener" , 37 "addLifecycleListener" , 38 "org.apache.catalina.LifecycleListener" ); 39 40 // Executor 41 digester.addObjectCreate("Server/Service/Executor" , 42 "org.apache.catalina.core.StandardThreadExecutor" , 43 "className" ); 44 digester.addSetProperties("Server/Service/Executor" ); 45 46 digester.addSetNext("Server/Service/Executor" , 47 "addExecutor" , 48 "org.apache.catalina.Executor");
比如這里面的digester.addSetNext("Server","setServer","org.apache.catalina.Server")這句話,在Digester類中的實現(xiàn)如下:
1 public void addSetNext(String pattern, String methodName, 2 String paramType) { 3 4 addRule(pattern, 5 new SetNextRule(methodName, paramType)); 6 7 }
實現(xiàn)的內(nèi)容就是把org.apache.catalina.Server以及setServer以SetNextRule的類型保存起來。看一下SetNextRule對象提供的方法,
其中end方法的實現(xiàn)如下:

public void end(String namespace, String name) throws Exception { // Identify the objects to be used Object child = digester.peek(0 ); Object parent = digester.peek(1 ); if (digester.log.isDebugEnabled()) { if (parent == null ) { digester.log.debug( "[SetNextRule]{" + digester.match + "} Call [NULL PARENT]." + methodName + "(" + child + ")" ); } else { digester.log.debug( "[SetNextRule]{" + digester.match + "} Call " + parent.getClass().getName() + "." + methodName + "(" + child + ")" ); } } if (methodName.equals("setServer" )){ System.out.println( "111111111111111111" ); } // Call the specified method IntrospectionUtils.callMethod1(parent, methodName, child, paramType, digester.getClassLoader()); }
在這里通過反射實現(xiàn)的方法調(diào)用。大家可能困惑到底是在哪發(fā)出rule.end調(diào)用動作的呢?下面還是要看一下Digester類,igester繼承了org.xml.sax.ext.DefaultHandler2類,其中有一個endElement方法,這個方法在讀完XML中每個Element的時候執(zhí)行,看一下endElement方法在Digester中的實現(xiàn):

@Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { boolean debug = log.isDebugEnabled(); if (debug) { if (saxLog.isDebugEnabled()) { saxLog.debug( "endElement(" + namespaceURI + "," + localName + "," + qName + ")" ); } log.debug( " match='" + match + "'" ); log.debug( " bodyText='" + bodyText + "'" ); } // Parse system properties bodyText = updateBodyText(bodyText); // the actual element name is either in localName or qName, depending // on whether the parser is namespace aware String name = localName; if ((name == null ) || (name.length() < 1 )) { name = qName; } // Fire "body" events for all relevant rules List<Rule> rules = matches.pop(); if ((rules != null ) && (rules.size() > 0 )) { String bodyText = this .bodyText.toString(); for ( int i = 0; i < rules.size(); i++ ) { try { Rule rule = rules.get(i); if (debug) { log.debug( " Fire body() for " + rule); } rule.body(namespaceURI, name, bodyText); } catch (Exception e) { log.error( "Body event threw exception" , e); throw createSAXException(e); } catch (Error e) { log.error( "Body event threw error" , e); throw e; } } } else { if (debug) { log.debug( " No rules found matching '" + match + "'." ); } if (rulesValidation) { log.warn( " No rules found matching '" + match + "'." ); } } // Recover the body text from the surrounding element bodyText = bodyTexts.pop(); if (debug) { log.debug( " Popping body text '" + bodyText.toString() + "'" ); } // Fire "end" events for all relevant rules in reverse order if (rules != null ) { for ( int i = 0; i < rules.size(); i++ ) { int j = (rules.size() - i) - 1 ; try { Rule rule = rules.get(j); if (debug) { log.debug( " Fire end() for " + rule); } if (name.equals("setServer" )){ System.out.println( "1222" ); } rule.end(namespaceURI, name); } catch (Exception e) { log.error( "End event threw exception" , e); throw createSAXException(e); } catch (Error e) { log.error( "End event threw error" , e); throw e; } } } // Recover the previous match expression int slash = match.lastIndexOf('/' ); if (slash >= 0 ) { match = match.substring(0 , slash); } else { match = "" ; } }
主要功能就是找出對應(yīng)的rule來逐一調(diào)用rule.end方法。根據(jù)在Catalina.java類中digester添加的rule,就執(zhí)行到了StandardServer類中的addService方法,設(shè)置的server對象,這部分內(nèi)容很重要。 ?
configFile:返回配置文件conf/server.xml文件。在獲取配置文件conf/server.xml出錯的時候,就嘗試去獲取server-embed.xml文件,如果都不存在,那么直接返回。記錄日志。
?initStreams:這個方法很簡單只是做了一個tomcat自定義的流的重定向,
getServer().init:設(shè)置一下server的狀態(tài),然后初始化網(wǎng)絡(luò)配置。
OK,load方法就說完了,很長。
然后在start方法中啟動server。至于start方法,我們不再本文中說明,等在以后的章節(jié)會專門介紹Server。
然后在在當期運行環(huán)境中注冊一個ShutdownHook,該鉤子的作于就是當程序結(jié)束時候,將Catalina程序shutdown。
到此為止,start方法就算是說完了。其中主要內(nèi)容就是如何構(gòu)造一個server對象。在以后會展開說明Server對象。
Stop :另外一個被外部調(diào)用的方法就是stop方法,看一下stop方法的代碼實現(xiàn):

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( "Catalina.stop" , e); } }
首先要移除在start方法中注冊的鉤子,否則在程序結(jié)束以后再次觸發(fā)鉤子中定義的事件,肯定會出錯。然后就獲取server對象,檢查狀態(tài),如果在運行那么停止,然后將資源釋放。stop方法簡單很多。
stopServer: 先檢查Server對象是否存在,如果不存在就創(chuàng)建一個新的,然后關(guān)閉server以及Server中定義的socket。
?
Catalina中的內(nèi)容大概就這么多了,很不過癮的地方就是內(nèi)容很多,沒有辦法全部展開,尤其是實現(xiàn)Server接口的Server對象,構(gòu)建server的方法,希望在下面的章節(jié)中把如何通過Digester構(gòu)建server,以及與次有很重要關(guān)系的Tomca的結(jié)構(gòu)比如server,services,connector,container等說清楚。
如果有不正確的地方請指正。大家共同學習。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
