Tomcat配置文件解析与Digester

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

public void addObjectCreate(String pattern, String className,
                            String attributeName) {
    addRule(pattern,
            new ObjectCreateRule(className, attributeName));
}

setProperties对应的是SetPropertiesRule。

public void addSetProperties(String pattern) {
    addRule(pattern,
            new SetPropertiesRule());
}

setNext对应的Rule是SetNextRule

在解析过程中,SetNextRule实质上,是会通过反射,调用对应方法。下图是在执行Catalina的一个setServer的Rule


对应setProperties的执行,也是通过反射,设置对应属性的值。例如,下图是在设置Server的port属性值。


这时,你可能会问,那这些对象设置属性,创建新对象等,他们之间又是如何关联起来的呢?

看这里,在Digester内部维护了一个stack结构,从下到上,越向上的层级越高。如下图,从Catalina,向上到Server,再到Service


进行Set的时候,会从顶向下,两个相邻的,即为父子关系,从而进行parent的child设置。

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

    // Call the specified method
    IntrospectionUtils.callMethod1(parent, methodName,
            child, paramType, digester.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文件,并生成配置文件对应的各个组件对象,之后各个对象开始启动,其生命周期开始。

更多请参考Digester文档。

ref:

https://wiki.apache.org/commons/Digester/WhyUseDigester

http://commons.apache.org/proper/commons-digester/guide/core.html

欢迎查看历史文章了解Tomcat原理与常见问题。

关注Tomcat那些事儿,发现更多精彩文章!了解各种常见问题背后的原理与答案。深入源码,分析细节,内容原创,欢迎关注。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值