浅析Tomcat之Digester

Bootstrap在实例化之后会实例化一个Catalina的实例.当命令是启动的时候,就调用了Catalina的start来启用服务器.也就是构造并实例化了Tomcat的连接器和容器.这个所谓的连接器和容器也是Java编码,在Tomcat的实现之内.他们之间的关系并不是通过硬编码来实现的.而是通过了Server.xml等配置文件.而Digester就是用来解析xml配置文件,并根据他们的关系来生成和配置文件的属性.

首先,我们应该知道的是Digester是apache基金会的一个开源项目.它能够根据编码人员定义的规则去解析XML并生成和配置相应的属性.Digester是它的主类.对于XML文档中的每个元素,它的对象会检查是否要执行预定义的事件.这些事件也就是继承了Rule的类,Rule类有2个方法,begin和end.当Digester实例遇到某个模式的开始标签的时候调用相应Rule对象的begin方法,遇到结束的话调用相应Rule对象的end方法.这一系列的解析过程是一个类似堆栈的过程.Digester对象中有一个stack属性来持有这些中间的对象.了解了上述规则后,我们开始看Catalina的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
try {
	getServer().start();
} catch (LifecycleException e) {
	log.error("Catalina.start: ", e);
}

刚刚开始启动的时候getServer返回的是空,于是调用Catalina的load方法,这个方法里面构造出Server等.getServer().start()是启动服务器.这里指谈load方法的细节,它是通过Digester,解析Server.xml文件并构造出Server对象及设置其中属性,并出示话Server.

Digester digester = createStartDigester();

//省略文件的读取.

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

getServer().setCatalina(this);

// Stream redirection
initStreams();

// Start the new server
try {
	getServer().init();
} 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);
	}

}

 上述是load方法的主体代码.createStartDigester是创建并设置它的事件(Rule)属性.接着digester.push(this)就是在解析Server.xml之前,将Catalina对象放到digester解析对象的堆栈顶.此时这个对象栈只有Catalina对象这个元素.接着是digester.parse(inputSource)对文件进行解析.后续的代码是Server的初始化,此处不做赘述.

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");
//省略一些Rule规则.

      上述代码是createStartDigester方法的主要代码.addObjectCreate的意思是匹配xml中的根元素Server标签,按照它的className属性的配置创建一个对象放入栈顶,如果Server标签没有className属性则默认是org.apache.catalina.core.StandardServer类的对象. addSetProperties方法是设置Server对应的属性,也就是匹配它的属性与该对象的属性,调用对象的set方法.addSetNext是调用栈顶的第二个元素的setServer方法把栈顶元素作为参数传入,其中参数类型是org.apache.catalina.Server.上面所描述的是规则的使用.上面3个方法都是添加对应规则的方法.

//addObjectCreate所使用的规则ObjectCreateRule的begin方法
public void begin(String namespace, String name, Attributes attributes)
		throws Exception {

	// Identify the name of the class to instantiate
	String realClassName = className;
	if (attributeName != null) {
		String value = attributes.getValue(attributeName);
		if (value != null) {
			realClassName = value;
		}
	}
	if (digester.log.isDebugEnabled()) {
		digester.log.debug("[ObjectCreateRule]{" + digester.match +
				"}New " + realClassName);
	}

	if (realClassName == null) {
		throw new NullPointerException("No class name specified for " +
				namespace + " " + name);
	}

	// Instantiate the new object and push it on the context stack
	Class clazz = digester.getClassLoader().loadClass(realClassName);
	Object instance = clazz.newInstance();
	digester.push(instance);
}

//addSetNext所使用的规则SetNextRule的end方法
public void end(String namespace, String name) throws Exception {

	// Identify the objects to be used
	Object child = digester.peek(0);
	Object parent = digester.peek(1);
	if (digester.log.isDebugEnabled()) {
		if (parent == null) {
			digester.log.debug("[SetNextRule]{" + digester.match +
					"} Call [NULL PARENT]." +
					methodName + "(" + child + ")");
		} else {
			digester.log.debug("[SetNextRule]{" + digester.match +
					"} Call " + parent.getClass().getName() + "." +
					methodName + "(" + child + ")");
		}
	}

	// Call the specified method
	IntrospectionUtils.callMethod1(parent, methodName,
			child, paramType, digester.getClassLoader());

}
//SetPropertiesRule的begin方法比较长就不贴出.

     上述代码是addObjectCreate和addSetNext所使用规则的主要起作用的方法,通过代码很容易看出addObjectCreate的作用就是Instantiate the new object and push it on the context stack而addSetNext的作用就是调用栈顶第一个元素的方法并把栈顶元素传入.

有了对Digester的了解,想要从配置文件Server.xml中探寻Catalina初始化Server,及之中组件的关系就不是什么难事了.

 

首发于泛泛之辈 - http://www.lihongkun.com/archives/86

你可能感兴趣的:(Tomcat)