Tomcat用户一定都知道其包含一个名为server.xml的配置文件。毕竟都在这里改过端口或者以目录形式部署过应用。
那这个配置文件里的配置信息又是如何对应到Tomcat中的具体对象的。开发的朋友马上会在大脑里浮现出Dom4j、Jdom、JAXB、SAX...一系列的XML解析工具。
不过,这里Tomcat用的并不是上面提到的这几位,而是Apache社区自己的XML解析工具Digester.
在Digester的Wiki里,这样介绍自己:
Many projects read XML configuration files to provide initialization of various Java objects within the system. There are several ways of doing this, and the Digester component was designed to provide a common implementation that can be used in many different projects.
我们来看Tomcat内部对于Digester是如何使用的。
在Catalina类内,我们会发现许多类似这样的代码:
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service");digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
以上这些是配置,最终的解析是通过parse这样一个方法
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource);
那是不是整个parse就是解析配置文件是否非法呢? Of course Not.
对应上面标红的addObjectCreate, addSetProperties,这些操作的背后,其实是添加了一系列的Rule。
这是个抽象类,对于上面添加的不同的Rule,有不同的实现。
其中,create对应的实现是ObjectCreateRule
addObjectCreate(String pattern, String className, String attributeName) { addRule(pattern, (className, attributeName)); }
setProperties对应的是SetPropertiesRule。
addSetProperties(String pattern) { addRule(pattern, ()); }
setNext对应的Rule是SetNextRule
在解析过程中,SetNextRule实质上,是会通过反射,调用对应方法。下图是在执行Catalina的一个setServer的Rule
对应setProperties的执行,也是通过反射,设置对应属性的值。例如,下图是在设置Server的port属性值。
这时,你可能会问,那这些对象设置属性,创建新对象等,他们之间又是如何关联起来的呢?
看这里,在Digester内部维护了一个stack结构,从下到上,越向上的层级越高。如下图,从Catalina,向上到Server,再到Service
进行Set的时候,会从顶向下,两个相邻的,即为父子关系,从而进行parent的child设置。
end(String namespace, String name) Exception { Object child = .peek(); Object parent = .peek(); IntrospectionUtils.(parent, , child, , .getClassLoader()); }
而对应的属性设置的时候,则是从stack里取最上面的一个,反射进行属性设置。
整个stack的最底层,则是Catalina,在获取配置文件输入流之后进行的push操作
public void push(Object object) {
if (stack.size() == 0) {
root = object;
}
stack.push(object);
}
Digester整体的设计思路,在XML元素开始,创建Object的rule执行时,把新创建的对象push到stack中。对象会保留在stack中,直到XML元素的结尾出现。
Rule的解析过程主要涉及到上面Rule类的几个方法
begin 元素开始时会执行
body 嵌套的元素匹配时执行
end 元素结束执行
finish 整个parse结束时调用
在parse结束之后,整个xml内容对应的Java对象已经创建成功,开动吧!
在Tomcat内部,Digester用在解析server.xml配置文件,MBean的描述文件,TLD文件校验等等。
具体用法也比较简单,new 一个Digester对象,然后设置具体的对象Rule,类似于各类的match规则,之后parse即可。
比如下面这个xml文件
<?xml version="1.0" encoding="UTF-8"?>
<foo name="The Parent">
<bar id="123" title="The First Child"/>
<bar id="456" title="The Second Child"/>
</foo>
解析的Digester代码只有这些
Digester digester = new Digester();
digester.setValidating( false );
digester.addObjectCreate( "foo", "mypackage.Foo" );
digester.addSetProperties( "foo" );
digester.addObjectCreate( "foo/bar", "mypackage.Bar" );
digester.addSetProperties( "foo/bar" );
digester.addSetNext( "foo/bar", "addBar", "mypackage.Bar" );
InputStream is = digester.getClass().getClassLoader().getResourceAsStream("mytest.xml");
Foo foo = digester.parse(is);
当然,具体XML文件需要对应的Java类还是需要的。
总结一下,Tomcat会在启动时,通过Digester读取解析server.xml文件,并生成配置文件对应的各个组件对象,之后各个对象开始启动,其生命周期开始。