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()的代码


	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()中。

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):


	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");

	}

从方法中可以知道,主要完成以下几个操作:

  • 配置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文件,同时实例化对象,调用方法设置层次关系,很强大,但是这也增加了阅读的难度。

在设置规则完成后,调用下面代码执行解析动作


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数组的一员,直接打印对象对吧输出结果,在下一篇中验证。

初始化完成后,返回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的角色扮演。

欢迎吐槽...

慢慢走下去,路就会清晰,再看一眼,在清晰一点

转载于:https://my.oschina.net/douglas/blog/159771

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值