`
刘琛颖
  • 浏览: 50253 次
  • 性别: Icon_minigender_1
  • 来自: 成都
最近访客 更多访客>>
社区版块
存档分类
最新评论

Tomcat启动部分源代码分析(三) -- 载入

阅读更多

二. 载入

2. Bootstrap的#Bootstrap#load(String[] arguments)方法。

方法的源代码如下述所示:

	private void load(String[] arguments) throws Exception {
		// 要调用的方法名
		String methodName = "load";
		// 参数名
		Object param[];
		// 参数值
		Class paramTypes[];
		if (arguments == null || arguments.length == 0) {
			paramTypes = null;
			param = null;
		} else {
			paramTypes = new Class[1];
			paramTypes[0] = arguments.getClass();
			param = new Object[1];
			param[0] = arguments;
		}
		Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes);
		if (log.isDebugEnabled())
			log.debug("Calling startup class " + method);
		
		// 这里会使程序产生两个分支,调用Catalina#load()和Catalina#load(String args[])两个方法。
		method.invoke(catalinaDaemon, param);

	}



这时就该轮到查看Catalina的两个load()方法了。先看看带参数的版本:

	public void load(String args[]) {

		try {
			if (arguments(args))
				load();
		} catch (Exception e) {
			e.printStackTrace(System.out);
		}
	}



代码很短,在内部还调用了无参数的load()版本。在这之前,先来看一下arguments()方法:

	protected boolean arguments(String args[]) {

		boolean isConfig = false;

		if (args.length < 1) {
			usage();
			return (false);
		}

		for (int i = 0; i < args.length; i++) {
			if (isConfig) {
				// server.xml的路径
				configFile = args[i];
				isConfig = false;
			} else if (args[i].equals("-config")) {
				// 下一个参数是server.xml的路径
				isConfig = true;
			} else if (args[i].equals("-nonaming")) {
				// 取消naming支持
				setUseNaming(false);
			} else if (args[i].equals("-help")) {
				// 帮助
				usage();
				return (false);
			} else if (args[i].equals("start")) {
				// 开启
				starting = true;
				stopping = false;
			} else if (args[i].equals("stop")) {
				// 关闭
				starting = false;
				stopping = true;
			} else {
				usage();
				return (false);
			}
		}

		return (true);

	}



主要对可以使用的一些参数进行了一些判断,如果参数有误就返回false。

如果arguments()返回true,两个分支就合二为一了,都进入了无参数的load()。

下面来重点看下这个方法:

	public void load() {

		long t1 = System.nanoTime();

		// 初始化Catalina环境路径,CatalinaHome和CatalinaBase
		initDirs();

		// 初始化naming
		initNaming();

		// 创建一个Digester对象,主要用于xml文件的读取,本实例中的Digester用于server.xml的读取。
		// 在createStartDigester()中队server.xml中的xml样式与具体的类进行了绑定。
		Digester digester = createStartDigester();

		InputSource inputSource = null;
		InputStream inputStream = null;
		File file = null;
		try {
			// server.xml文件
			file = configFile();
			inputStream = new FileInputStream(file);
			inputSource = new InputSource("file://" + file.getAbsolutePath());
		} catch (Exception e) {
			;
		}
		if (inputStream == null) {
			try {
				// 在jar包中寻找server.xml
				inputStream = getClass().getClassLoader().getResourceAsStream(getConfigFile());
				inputSource = new InputSource(getClass().getClassLoader().getResource(
						getConfigFile()).toString());
			} catch (Exception e) {
				;
			}
		}

		if (inputStream == null) {
			try {
				// 在jar包中寻找config-embed.xml
				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());
			return;
		}

		try {
			inputSource.setByteStream(inputStream);
			digester.push(this);
			digester.parse(inputSource);
			inputStream.close();
		} catch (Exception e) {
			log.warn("Catalina.start using " + getConfigFile() + ": ", e);
			return;
		}

		// 重定向输出流和错误流
		initStreams();

		if (server instanceof Lifecycle) {
			try {
				// Server初始化
				server.initialize();
			} catch (LifecycleException e) {
				log.error("Catalina.start", e);
			}
		}

		long t2 = System.nanoTime();
		if (log.isInfoEnabled())
			log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");

	}



initDirs()和initNaming()主要是值的设定,就不细说了。

createStartDigester()方法主要是实例化一个Digester对象,并提供了xml中的模式与java类对象的绑定。对Digester并没有什么研究,只是通过JavaDoc大致了解了下程序的运作过程。

	protected Digester createStartDigester() {
		long t1 = System.currentTimeMillis();
		// 初始化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);
		digester.setClassLoader(StandardServer.class.getClassLoader());

		// 三个参数分别为:1. xml标签的名字;2. 默认的Java类名;3.如果标签中有这个属性,那么它就覆盖掉默认的Java类名
		// 具体到下边的这条语句,就是处理"Server"标签,并实例化一个StandardServer类的实例
		// 如果Server标签有一个叫className的属性,就会覆盖掉默认的Java类型。
		digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
		
		// 设定一个SetPropertiesRule,这个规则是用于堆栈顶端元素的属性设置
		digester.addSetProperties("Server");

		// 三个参数分别为:1.xml标签名;2.要设定的"父对象"(与继承无关)的方法名;3.方法参数的Java类型
		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, "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, "className");
		digester.addSetProperties("Server/Service/Listener");
		digester.addSetNext("Server/Service/Listener", "addLifecycleListener",
				"org.apache.catalina.LifecycleListener");

		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, "className");
		digester.addSetProperties("Server/Service/Connector/Listener");
		digester.addSetNext("Server/Service/Connector/Listener", "addLifecycleListener",
				"org.apache.catalina.LifecycleListener");

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

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

	}



虽然没有研究Digester,但是大致的过程还是可以猜出来的,首先,Digester需要一个堆栈的顶端元素,在Catalina#load()里压入了Catalina类本身,后边会介绍到,然后会根据一系列addObjectCreate,addSetProperties,addSetNext设定类与xml的绑定等。比如GlobalNamingResources这个属性,

digester.addObjectCreate("Server/GlobalNamingResources", "org.apache.catalina.deploy.NamingResources");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources", "setGlobalNamingResources", "org.apache.catalina.deploy.NamingResources");



上边的3条语句设定了遇到Server/GlobalNamingResources,实例化一个NamingResources类的对象,并调用setGlobalNamingResources将这个对象与父对象关联。而它的父对象很明显就是"Server"定义的对象,就是StandardServer类的对象。而StandardServer又与栈定对象相关联(通过调用Catalina的#setServer设定的)。所以解析完server.xml之后,Catalina的成员变量"server(定义为:protected Server server = null;)"就应该被赋值了,并且xml中的所有元素在server下边都有对应的对象相关联。

这里有必要介绍下server.xml中定义的各个元素的作用(引用自http://jackycheng2007.iteye.com/blog/188401)

Server
"Server" 是单例的,代表整个JVM,它可能包含几个"Service"实例。 "Server" 从指定的端口监听关闭命令。
<Server port="8005" shutdown="SHUTDOWN">

"Service"
一个"Service"是一个或者多个"Connectors"的集合,他们共享一个"Container"(所以多个web应用在整个容器内是可见的)。通常但不必须,Container是一个"Engine"。
<Service name="Catalina">
可以把Service看成媒介,它存活在一个Server里面,把一个或几个Connectors绑定在一个Engine上。
所以,在server.xml 中"Service"是Server的子组件。Connector 和 Engine 是Service的子组件。

"Connector"
一个Connector将在某个指定端口上侦听客户请求,并将获得的请求交给Engine来处理,从Engine处获得回应并返回客户
TOMCAT有两个典型的Connector,一个直接侦听来自browser的http请求,一个侦听来自其它WebServer的请求
Coyote Http/1.1 Connector 在端口8080处侦听来自客户browser的http请求
Coyote JK2 Connector 在端口8009处侦听来自其它WebServer(Apache)的servlet/jsp代理请求.
<Connector port="8080" protocol="HTTP/1.1" maxThreads="150" connectionTimeout="20000" redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

"Engine"
一个"Engine"代表了一个service的请求管道。一个service可能有多个Connector, 这个Engine接收并处理来自这些Connector的请求(requests ),并且将处理反馈(response )传给相应的connector,进而传给客户端。

"Host "
一个Host 是域名(e.g. www.yourcompany.com)和tomcat server的联系体。 一个Engine可以包含多个Host,而且Host 可以支持网络别名如yourcompany.com 和 abc.yourcompany.com。所以,Host是Engine的子组件。

"Context"
一个Context代表一个web应用。一个Host可以包含多个 Context, 他们都有一个唯一的路径。

再回到代码。

digester初始化结束后,就是一系列的server.xml的寻找工作了,当定位了server.xml之后,就要开始对它的解析了。

		try {
			inputSource.setByteStream(inputStream);
			digester.push(this);
			digester.parse(inputSource);
			inputStream.close();
		} catch (Exception e) {
			log.warn("Catalina.start using " + getConfigFile() + ": ", e);
			return;
		}



这一段就是解析的代码,很明显,digester把Catalina对象压到了栈顶。

然后,调用了这样的一句话:server.initialize();前面已经提到过了,server对象已经被实力化为StandardServer类的对象,那么看一下StandardServer#initialize()都做了什么:

	public void initialize() throws LifecycleException {
		if (initialized) {
			log.info(sm.getString("standardServer.initialize.initialized"));
			return;
		}

		// 通知所有Lifecycle事件监听器"INIT_EVENT"事件的发生
		lifecycle.fireLifecycleEvent(INIT_EVENT, null);
		initialized = true;

		// JMX相关的配置
		if (oname == null) {
			try {
				oname = new ObjectName("Catalina:type=Server");
				Registry.getRegistry(null, null).registerComponent(this, oname, null);
			} catch (Exception e) {
				log.error("Error registering ", e);
			}
		}

		try {
			ObjectName oname2 = new ObjectName(oname.getDomain() + ":type=StringCache");
			Registry.getRegistry(null, null).registerComponent(new StringCache(), oname2, null);
		} catch (Exception e) {
			log.error("Error registering ", e);
		}

		// Service初始化,默认只有一个org.apache.catalina.core.StandardService实例
		// 见Catalina#createStartDigester()
		for (int i = 0; i < services.length; i++) {
			services[i].initialize();
		}
	}



首先,lifecycle.fireLifecycleEvent(INIT_EVENT, null);通知了StandardServer包含的事件监听器有一个"INIT_EVENT"事件到来了,默认有如下四个监听器:
org.apache.catalina.core.AprLifecycleListener)(Apache Portable Runtime -- APR)
org.apache.catalina.core.JasperListener(Java Server Pages -- JSP)
org.apache.catalina.mbeans.ServerLifecycleListener
org.apache.catalina.mbeans.GlobalResourcesLifecycleListener

四个监听器的INIT_EVENT方法处理最后涉及到了native方法的调用,就不再详细说了。

然后,对JMX进行了初始化,JMX暂时不懂。

最后,对Service进行了初始化,默认只有一个StandardService#initialize()

	public void initialize() throws LifecycleException {
		if (initialized) {
			if (log.isInfoEnabled())
				log.info(sm.getString("standardService.initialize.initialized"));
			return;
		}
		initialized = true;

		if (oname == null) {
			try {
				// 容器
				Container engine = this.getContainer();
				// 名字,默认是Catalina
				domain = engine.getName();
				oname = new ObjectName(domain + ":type=Service,serviceName=" + name);
				this.controller = oname;
				// 注册JMX,我发誓看完这个就去看JMX
				Registry.getRegistry(null, null).registerComponent(this, oname, null);
				
				// Executors的注册
				Executor[] executors = findExecutors();
				for (int i = 0; i < executors.length; i++) {
					ObjectName executorObjectName = new ObjectName(domain + ":type=Executor,name="
							+ executors[i].getName());
					Registry.getRegistry(null, null).registerComponent(executors[i],
							executorObjectName, null);
				}

			} catch (Exception e) {
				log.error(sm.getString("standardService.register.failed", domain), e);
			}

		}
		if (server == null) {
			ServerFactory.getServer().addService(this);
		}

		synchronized (connectors) {
			// 这里是Connector的初始化,默认有HTTP/1.1(8080),AJP/1.3(8009)两个
			// <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
			// <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
			for (int i = 0; i < connectors.length; i++) {
				// ServerSocket初始化
				connectors[i].initialize();
			}
		}
	}



通过上述的步骤,Tomcat的ServerSocket已经建立了起来,load的过程也结束了。

分享到:
评论

相关推荐

    Tomcat 源代码调试笔记 - 看不见的 Shell1

    通过上述分析,我们可以理解在Tomcat中动态添加过滤器的原理和挑战,以及如何通过源代码调试和分析找到解决问题的途径。这对于理解和优化Tomcat应用的性能、安全性和可扩展性至关重要。对于开发者来说,掌握这类技术...

    apache-tomcat-8.5.99-windows-x64.zip

    "apache-tomcat-8.5.99-windows-x64.zip"表明这是专为Windows 64位操作系统设计的。在64位系统上运行64位版本的Tomcat可以充分利用系统的内存资源,对于处理大型应用或高并发场景更为有利。 **4. 安装与配置** 解压...

    tomcat-redis-session-manager-1.2-tomcat-6&7

    标题 "tomcat-redis-session-manager-1.2-tomcat-6&7" 指的是一个用于在Tomcat服务器中集成Redis作为session管理器的组件。这个组件使得Web应用程序可以利用Redis分布式缓存系统来存储和管理用户的会话数据,从而...

    apache-tomcat-8.5.98-windows-x64.zip

    这个压缩包 "apache-tomcat-8.5.98-windows-x64.zip" 包含了适用于Windows 64位操作系统的Apache Tomcat 8.5.98版本。该版本是Tomcat的稳定版本之一,提供了对Java EE 7规范的支持。 Apache Tomcat的核心功能是作为...

    tomcat-redis-session-manager-master-2.0.0

    tomcat-redis-session-manager-2.0.0.jar jedis-2.5.2.jar commons-pool2-2.2.jar 2.修改 conf 目录下的 context.xml 文件 &lt;Valve className="com.orangefunction.tomcat.redissessions....

    Tomcat7下载(apache-tomcat-7.0.85)

    Tomcat7下载(apache-tomcat-7.0.85)Tomcat7下载(apache-tomcat-7.0.85)Tomcat7下载(apache-tomcat-7.0.85)Tomcat7下载(apache-tomcat-7.0.85)

    apache-tomcat-9.0.82-windows-x64.zip

    3. **目录结构**:解压后的`apache-tomcat-9.0.82`目录会包含以下几个关键部分: - `bin`:包含启动和停止Tomcat的脚本。 - `conf`:存放服务器配置文件。 - `lib`:存储Tomcat运行所需的JAR文件。 - `webapps`...

    官方原版apache-tomcat-8.5.51-windows-x64.zip 64位

    该压缩包"apache-tomcat-8.5.51-windows-x64.zip"包含以下关键组件: 1. **bin目录**:这个目录包含了用于启动、停止和管理Tomcat的各种脚本,如`catalina.bat`(Windows批处理文件)和`startup.sh`(Unix/Linux ...

    tomcat-juli.jar和tomcat-juli-adapters.jar

    总结来说,`tomcat-juli.jar`和`tomcat-juli-adapters.jar`是Tomcat日志系统的重要组成部分,它们为开发者提供了强大的日志管理和适配功能,使得在处理复杂的服务器环境和应用问题时,能够获得足够的信息支持。...

    tomcat共享session tomcat-redis-session-manager-2.0.0.jar包下载

    tomcat-redis-session-manager-2.0.0.jar包,不用自己打包了,tomcat共享session到redis中,解决分布式应用的状态问题。

    tomcat-redis-session-manager-master.rar

    1、tomcat-redis-session-manager-master见目录中的说明 2、示例程序启动顺序 启动redis,启动tomcat,启动nginx 3、访问:http://127.0.0.1 显示nginx欢迎页面 访问:http://127.0.0.1:8088/session 显示tomcat1...

    Apache-tomcat-7.0.109-Windows-x64

    在本文中,我们将深入探讨与"Apache-tomcat-7.0.109-Windows-x64"相关的知识,包括Tomcat的概述、版本7.0.109的特点、在Windows 64位系统上的安装与配置,以及Java环境的设置等关键内容。 首先,让我们了解Tomcat的...

    tomcat-jdbc数据源所需jar包tomcat-jdbc.jar+tomcat-juli.jar

    在处理数据库连接方面,Tomcat提供了一种高效且可管理的数据源实现,名为“tomcat-jdbc数据源”。这个数据源是Tomcat内建的一种连接池,它在性能和内存管理上比标准的Java JDBC连接池更优秀,尤其适用于高并发的Web...

    apache-tomcat-10.0.23-windows-x64.zip

    在"apache-tomcat-10.0.23-windows-x64.zip"这个压缩包中,你会找到以下主要文件和目录: 1. `bin`目录:包含了启动和停止Tomcat的脚本,如`catalina.bat`(Windows批处理文件)和`startup.sh`(Unix/Linux shell...

    apache-tomcat-9.0.74-windows-x64

    下载并解压`apache-tomcat-9.0.74`压缩包后,用户需要将Tomcat添加到系统环境变量PATH中,以便于命令行启动和停止服务。配置`conf/server.xml`文件可以定制服务器端口、上下文路径等设置。此外,`webapps`目录用于...

    最新版windows apache-tomcat-9.0.68-windows-x64.zip

    Apache Tomcat是一款开源的Java ...以上是关于`apache-tomcat-9.0.68-windows-x64.zip`的基本介绍,包括其组成部分、特性、安装配置方法以及安全管理与优化策略。希望这些信息对理解和使用Apache Tomcat有所帮助。

    最新版windows apache-tomcat-8.5.70-windows-x64.zip

    这个最新的版本“apache-tomcat-8.5.70-windows-x64.zip”是专门为Windows操作系统设计的64位版本。在本文中,我们将深入探讨Apache Tomcat 8.5.70在Windows环境下的安装、配置、管理和优化。 首先,安装过程通常...

    分布式session分享tomcat-redis-session-manager-master

    tomcat-redis-session-manager-master为tomcat集群分布式session分享功能,详细信息见http://blog.csdn.net/fengshizty?viewmode=list对应内容

    tomcat-redis-session-manager-1.2-tomcat-6.jar

    用于配置 tomcat-redis-session-manager

    tomcat-native-1.2.25-src-build

    "tomcat-native-1.2.25-src-build" 指的是该扩展的源代码版本1.2.25,用于编译生成适用于不同操作系统的本地库文件。 【编译过程】:下载的源码包"tomcat-native-1.2.25-src-build"需要经过编译才能生成适用于目标...

Global site tag (gtag.js) - Google Analytics