2013-09-07
週末來啊,宅的日子又到了... 咱們知道tomcat的基本是容器,經過容器來完成servlet容器的設計。從上一篇blog中看到了tomcat的結構框架圖,圖中容器嵌套,組件組合,tomcat如何設置他們的結構關係,如何將他們融合成能完成Servlet容器更能的東西,經過對tomcat6.0.x啓動過程的瞭解,咱們應該能夠知道tomcat如何組織他們的。
要想知道tomcat的啓動,首先得知道tomcat的啓動類時哪個,跟蹤啓動腳本會發現啓動類:org.apache.catalina.startup.Bootstrap,啓動以後能夠啓用eclipse的單步跟蹤分析啓動過程。
org.apache.catalina.startup.Bootstrap,用來啓動管理tomcat的運行,例如啓動、中止等,在Bootstrap的main函數中咱們能夠看到先判斷有沒有實例化過,而後調用init()函數,init()的代碼java
public void init() throws Exception { // Set Catalina path setCatalinaHome(); setCatalinaBase(); //構造所需的類加載器 initClassLoaders(); //設置當前線程的上下文類加載器爲catalinaLoader Thread.currentThread().setContextClassLoader(catalinaLoader); //設置安全的類加載器爲catalinaLoader SecurityClassLoad.securityClassLoad(catalinaLoader); // Load our startup class and call its process() method if (log.isDebugEnabled()) log.debug("Loading startup class"); //使用catalinaLoader加載器加載類org.apache.catalina.startup.Catalina(提供Startup and Shutdown shell) Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance(); //設置startupInstance的父加載器爲共享類加載器的 // 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; }
從代碼中能夠看出來再init()函數中完成了Servlet容器類加載器的設置,加載在web app用應用到的common、server、shared三個不一樣的類加載器,設置類加載器是處於安全的考慮,例如一個Context不能直接訪問另外一個Context,同時也是爲了共享jar,在initClassLoaders()方法中完成了3個不一樣類加載器對應目錄下面的jar包,類文件的查找加載。還有一個很重要的就是實例化org.apache.catalina.startup.Catalina,並經過反射來設置org.apache.catalina.startup.Catalina的父類加載器。完成設置類加載和實例化org.apache.catalina.startup.Catalina後,根據傳人蔘數來決定動做。在「start」參數選擇啓動servlet容器,會執行load(argments)方法和start()方法,load方法,負責調用org.apache.catalina.startup.Catalina.load()方法來加載配置文件,組織資源等,而後調用start方法來調用org.apache.catalina.startup.Catalina.start()方法,至此tomcat 完成啓動。其實只要內容是在org.apache.catalina.startup.Catalina.load()中和org.apache.catalina.startup.Catalina.start()中。web
org.apache.catalina.startup.Catalina 是真正負責管理tomcat的類,主要是解析server.xml,初始化組件,操做組件狀態,設置一個程序退出的Hook。Catalina 繼承自Embedded,而Embedded又繼承自StandardService,說明Catalina 是一個Service(這個有意思,難道tomcat只能有一個service,不太可能吧)。
在org.apache.catalina.startup.Bootstrap方法load()中調用了Catalina的load()方法,Catalina的方法load(argments):shell
public void load() { long t1 = System.nanoTime(); //catalina.home和catalina.base initDirs(); // Before digester - it may be needed //初始化命名 initNaming(); // Create and execute our Digester //建立將xml轉成對象的解析規則器 Digester digester = createStartDigester(); //搜尋配置文件 InputSource inputSource = null; InputStream inputStream = null; File file = null; try { //加載conf/server.xml配置文件 file = configFile(); inputStream = new FileInputStream(file); inputSource = new InputSource("file://" + file.getAbsolutePath()); } catch (Exception e) { ; } if (inputStream == null) { try { inputStream = getClass().getClassLoader().getResourceAsStream( getConfigFile()); inputSource = new InputSource(getClass().getClassLoader() .getResource(getConfigFile()).toString()); } catch (Exception e) { ; } } // This should be included in catalina.jar // Alternative: don't bother with xml, just create it manually. if (inputStream == null) { try { inputStream = getClass().getClassLoader().getResourceAsStream( "server-embed.xml"); inputSource = new InputSource(getClass().getClassLoader() .getResource("server-embed.xml").toString()); } catch (Exception e) { ; } } if ((inputStream == null) && (file != null)) { log.warn("Can't load server.xml from " + file.getAbsolutePath()); if (file.exists() && !file.canRead()) { log.warn("Permissions incorrect, read permission is not allowed on the file."); } return; } try { inputSource.setByteStream(inputStream); digester.push(this); digester.parse(inputSource); inputStream.close(); } catch (Exception e) { log.warn("Catalina.start using " + getConfigFile() + ": ", e); return; } // Stream redirection //設置輸出流設備 initStreams(); // Start the new server //啓動新server if (getServer() instanceof Lifecycle) { try { getServer().initialize(); } catch (LifecycleException e) { if (Boolean .getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) throw new java.lang.Error(e); else log.error("Catalina.start", e); } } long t2 = System.nanoTime(); if (log.isInfoEnabled()) log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms"); }
從方法中能夠知道,主要完成如下幾個操做:apache
其中重要點在Digester庫來解析conf/server.xml和初始化Server上面。 Digester digester = createStartDigester();建立一個對象解析server.xml。
createStartDigester():數組
protected Digester createStartDigester() { long t1 = System.currentTimeMillis(); // Initialize the digester Digester digester = new Digester(); digester.setValidating(false); digester.setRulesValidation(true); HashMap<Class, List<String>> fakeAttributes = new HashMap<Class, List<String>>(); ArrayList<String> attrs = new ArrayList<String>(); attrs.add("className"); fakeAttributes.put(Object.class, attrs); digester.setFakeAttributes(fakeAttributes); //設置ClassLoader確保在同一個jvm的命名空間中,能夠相互訪問 digester.setClassLoader(StandardServer.class.getClassLoader()); // 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.NamingResources"); digester.addSetProperties("Server/GlobalNamingResources"); digester.addSetNext("Server/GlobalNamingResources", "setGlobalNamingResources", "org.apache.catalina.deploy.NamingResources"); digester.addObjectCreate("Server/Listener", null, // MUST be specified // in the element "className"); digester.addSetProperties("Server/Listener"); digester.addSetNext("Server/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener"); digester.addObjectCreate("Server/Service", "org.apache.catalina.core.StandardService", "className"); digester.addSetProperties("Server/Service"); digester.addSetNext("Server/Service", "addService", "org.apache.catalina.Service"); digester.addObjectCreate("Server/Service/Listener", null, // MUST be // specified // in the // element "className"); digester.addSetProperties("Server/Service/Listener"); digester.addSetNext("Server/Service/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener"); // Executor digester.addObjectCreate("Server/Service/Executor", "org.apache.catalina.core.StandardThreadExecutor", "className"); digester.addSetProperties("Server/Service/Executor"); digester.addSetNext("Server/Service/Executor", "addExecutor", "org.apache.catalina.Executor"); digester.addRule("Server/Service/Connector", new ConnectorCreateRule()); digester.addRule("Server/Service/Connector", new SetAllPropertiesRule( new String[] { "executor" })); digester.addSetNext("Server/Service/Connector", "addConnector", "org.apache.catalina.connector.Connector"); digester.addObjectCreate("Server/Service/Connector/Listener", null, // MUST // be // specified // in // the // element "className"); digester.addSetProperties("Server/Service/Connector/Listener"); digester.addSetNext("Server/Service/Connector/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener"); // Add RuleSets for nested elements digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/")); digester.addRuleSet(new EngineRuleSet("Server/Service/")); digester.addRuleSet(new HostRuleSet("Server/Service/Engine/")); digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/")); digester.addRuleSet(ClusterRuleSetFactory .getClusterRuleSet("Server/Service/Engine/Host/Cluster/")); digester.addRuleSet(new NamingRuleSet( "Server/Service/Engine/Host/Context/")); // When the 'engine' is found, set the parentClassLoader. digester.addRule("Server/Service/Engine", new SetParentClassLoaderRule( parentClassLoader)); digester.addRuleSet(ClusterRuleSetFactory .getClusterRuleSet("Server/Service/Engine/Cluster/")); long t2 = System.currentTimeMillis(); if (log.isDebugEnabled()) log.debug("Digester for server.xml created " + (t2 - t1)); return (digester); }
方法中設置了tomcat容器組件層次之間的關係,Server解析,Service解析,Engine解析,Host解析等,Digester 是一個規則解析引擎,根據設定的規則來解析server.xml文件,同時實例化對象,調用方法設置層次關係,很強大,可是這也增長了閱讀的難度。tomcat
在設置規則完成後,調用下面代碼執行解析動做安全
digester.push(this);//壓入本類實例對象,提供解析生成容器組件組合宿主 digester.parse(inputSource);//解析server.xml
解析過程是很複雜的,能夠設置斷點跟蹤查看。
在解析完成以後,tomcat調用getServer().initialize();對server初始化,Server的標準實現StandardServer,StandardServer方法initialize()負責對Server初始化,跟蹤代碼發現主要是對註冊在Server下面的service數組進行初始化。
疑問:居然Server下面有service數組,那麼Catalina這個也實現了service接口的類在tomcat中充當怎樣的角色?簡單的控制角色嗎?那幹啥要實現service接口?
能夠經過驗證catalina是不是Server中service數組的一員,直接打印對象對吧輸出結果,在下一篇中驗證。app
初始化完成後,返回load方法,在返回到Bootstrap中,而後調用start方法來啓動容器,在start中調用Catalina中的start方法啓動容器。
Catalina的start()方法:框架
public void start() { if (getServer() == null) { load(); } if (getServer() == null) { log.fatal("Cannot start server. Server instance is not configured."); return; } long t1 = System.nanoTime(); // Start the new server if (getServer() instanceof Lifecycle) { try { ((Lifecycle) getServer()).start(); } catch (LifecycleException e) { log.error("Catalina.start: ", e); } } long t2 = System.nanoTime(); if (log.isInfoEnabled()) log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms"); try { // Register shutdown hook //添加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); } } } catch (Throwable t) { // This will fail on JDK 1.2. Ignoring, as Tomcat can run // fine without the shutdown hook. } if (await) { await();//等待線程結束(在catalina單線程中,等待結束) stop(); } }
方法中先判斷Server是否存在實例,若是不存在,掉load()方法,加載server.xml配置文件,解析建立對象,而後調用start()方法啓動容器,在server的start()方法中,跟initialize()方法也有,也是調用service數組中service的initialize()方法。在啓動容器後,添加一個Runtime.getRuntime().addShutdownHook(shutdownHook);監控程序異常退出時清理資源。
tomcat的啓動有不少信息,service的initialize()方法和驗證疑問以及service的start()方法會在接下來的blog中跟蹤...
tomcat啓動,首先設置類加載器,加載jar包和類,設置環境目錄,使用Digester庫解析server.xml生成容器組件層次關係,初始化Server對象,啓動Server,完成啓動過程,在這裏尚未講到ServerSocket部分,是Service中的內容,還有server.xml的文件內容,還有Server的角色扮演。eclipse
歡迎吐槽...
慢慢走下去,路就會清晰,再看一眼,在清晰一點