tomcat6.0.x啓動過程-tomcat 6.x 源碼閱讀

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

  • 配置Servlet容器目錄環境
  • 設置Naming(JNDI)
  • 使用Digester庫來解析conf/server.xml,並設置容器組件層次結構關係
  • 初始化Server

其中重要點在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

歡迎吐槽...

慢慢走下去,路就會清晰,再看一眼,在清晰一點

相關文章
相關標籤/搜索