文档章节

tomcat6.0.x启动过程-tomcat 6.x 源码阅读

douglaswei
 douglaswei
发布于 2013/09/07 12:04
字数 2123
阅读 397
收藏 9

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的角色扮演。

欢迎吐槽...

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

© 著作权归作者所有

douglaswei
粉丝 9
博文 61
码字总数 40667
作品 0
珠海
架构师
私信 提问
加载中

评论(2)

douglaswei
douglaswei 博主

引用来自“chengEmerson”的评论

大神啊,我好葱白你!

呃。。。,不知道肿么写了,呜呜呜
chengEmerson
chengEmerson
大神啊,我好葱白你!
tomcat6.0.x框架结构图-tomcat 6.x 源码阅读

2013-09-05 昨天搞域名,没有写blog,愧疚蛮久,还好域名搞定了...嘻嘻 继续行程,记录点滴...: tomcat6.0.x已经通过svn导入eclipse运行,接下来就是阅码之旅。,每个人阅读源码方式可能不尽相...

douglaswei
2013/09/06
1K
1
eclipse导入tomcat源码-tomcat 6.x 源码阅读

2013-09-03 : 从大学开始学习java到现在已经有3年多,工作有一年多,起初工作还是java方面的,最近切到python。心理总有一直不踏实的感觉,觉得缺少点东西,没有自己的想法,很少问自己为什么...

douglaswei
2013/09/04
1K
2
谈谈 Tomcat 架构及启动过程[含部署]

谈谈 Tomcat 架构及启动过程[含部署] ImportNew2018-01-061 阅读 Tomcat 原文出处: Rainstorm 这个题目命的其实是很大的,写的时候还是很忐忑的,但我尽可能把这个过程描述清楚。因为这是读...

ImportNew
2018/01/06
0
0
谈谈 Tomcat 架构及启动过程[含部署]

原文出处:Rainstorm 这个题目命的其实是很大的,写的时候还是很忐忑的,但我尽可能把这个过程描述清楚。因为这是读过源码以后写的总结,在写的过程中可能会忽略一些前提条件,如果有哪些比较...

Rainstorm
2018/01/06
0
0
MyEclipse 多项目对应配置多个Tomcat

MyEclipse 多项目对应配置多个Tomcat 如果多个项目放在一个Tomcat下,在MyEclipse启动Tomcat时,会把其他项目也一起启动,速度会慢。 现在设置多个项目,分别对应一个Tomcat。 如果你设置多个...

robinjiang
2013/11/21
282
0

没有更多内容

加载失败,请刷新页面

加载更多

如何快速为网站选择合适的SSL证书

随着HTTPS普及,越来越多用户开始采用SSL证书,来对HTTP进行加密,升级到HTTPS。但面对各种不同的SSL证书,用户应如何选择?安信SSL证书将为大家讲解: 一、按SSL证书类型选择 DV SSL证书:域...

安信证书
28分钟前
2
0
被嫌弃的eval和with

本文转载于:专业的前端网站➥被嫌弃的eval和with 前面的话   eval和with经常被嫌弃,好像它们的存在就是错误。在CSS中,表格被嫌弃,在网页中只是用表格来展示数据,而不是做布局,都可能被...

前端老手
31分钟前
4
0
Allegro非常实用的快捷键-PCB环境

立题简介: 内容:简单介绍Allegro绘制的PCB环境下的快捷键; 来源:实际使用得出; 作用:对Allegro绘制PCB快捷键进行介绍; PCB环境:Cadence 16.6; 立题详解: 对“allegro”板而言,其在...

demyar
38分钟前
1
0
润乾报表与 ActiveReport JS 功能对比

简介 润乾报表是用于报表制作的大型企业级报表软件,核心特点在于开创性地提出了非线性报表数学模型,采用了革命性的多源关联分片、不规则分组、自由格间运算、行列对称等技术,使得复杂报表...

泡泡糖儿
39分钟前
1
0
仿微信打飞机游戏网页版,基于cocos2d-js游戏引擎,在线试玩,内含源码

早几年研究cocos2d的demo项目,这个是基于cocos2d-js游戏引擎,整个游戏用js编写。 玩法:鼠标拖动飞机移动即可 试玩地址 源码地址 游戏截图: 文件说明 cocos2dx:游戏引擎 res:存放游戏素...

tanghc
41分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部