Tomcat

Tomcat的配置解析工具Apache Commons Dige

2018-10-10  本文已影响62人  晴天哥_王志

开篇

 最近开始看Tomcat的源码,在catalina. createStartDigester方法中,Tomcat开发人员采用了Digester来读取conf/server.xml文件,以前读取xml文件一般采用Dom4j和SAX,因此对Digester比较陌生,顺便就抽时间研究了一下Digester是如何解析xml文件的。

先简单阐述下Dom4j和SAX解析XML的区别:

Digester的来源

Digester工作原理:

以下的内容都是分享自文末参考文章的内容,对于一些API的使用非常建议直接下载jar包看源码注释

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-digester3</artifactId>
            <version>3.2</version>
        </dependency>

使用Digester的基本步骤

 了解基本步骤后,来看一个简单的示例,如下所示,是我们即将要解析的xml文件:

<foo name="The Parent">
    <bar id="123" title="The First Child" />
    <bar id="456" title="The Second Child" />
    <bar id="789" title="The Second Child" />
</foo>

 首先,创建两个java bean对应xml中的元素信息:

Foo类

package apache.commons.digester3.example.pojo;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @author http://www.cnblogs.com/chenpi/
 * @version 2017年6月3日
 */
public class Foo
{
    private String name;
    private List<Bar> barList = new ArrayList<Bar>();

    public void addBar(Bar bar)
    {
        barList.add(bar);
    }

    public Bar findBar(int id)
    {
        for (Bar bar : barList)
        {
            if (bar.getId() == id)
            {
                return bar;
            }
        }
        return null;
    }

    public Iterator<Bar> getBars()
    {
        return barList.iterator();
    }

    /**
     * @return the name
     */
    public String getName()
    {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name)
    {
        this.name = name;
    }

    /**
     * @return the barList
     */
    public List<Bar> getBarList()
    {
        return barList;
    }

    /**
     * @param barList the barList to set
     */
    public void setBarList(List<Bar> barList)
    {
        this.barList = barList;
    }
}

Bar类

package apache.commons.digester3.example.pojo;

/**
 * @author    http://www.cnblogs.com/chenpi/
 * @version   2017年6月3日
 */
public class Bar
{

    private int id;
    private String title;

    /**
     * @return the id
     */
    public int getId()
    {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(int id)
    {
        this.id = id;
    }

    /**
     * @return the title
     */
    public String getTitle()
    {
        return title;
    }

    /**
     * @param title the title to set
     */
    public void setTitle(String title)
    {
        this.title = title;
    }

}

 使用Digester解析xml:

package apache.commons.digester3.example.simpletest;

import java.io.IOException;

import org.apache.commons.digester3.Digester;
import org.xml.sax.SAXException;

import apache.commons.digester3.example.pojo.Bar;
import apache.commons.digester3.example.pojo.Foo;

/**
 * 
 * @author http://www.cnblogs.com/chenpi/
 * @version 2017年6月3日
 */

public class Main
{

    public static void main(String[] args)
    {

        try
        {
            //1、创建Digester对象实例
            Digester digester = new Digester();

            //2、配置属性值
            digester.setValidating(false);

            //3、push对象到对象栈
            //digester.push(new Foo());
            
            //4、设置匹配模式、规则
            digester.addObjectCreate("foo", "apache.commons.digester3.example.pojo.Foo");
            digester.addSetProperties("foo");
            digester.addObjectCreate("foo/bar", "apache.commons.digester3.example.pojo.Bar");
            digester.addSetProperties("foo/bar");
            digester.addSetNext("foo/bar", "addBar", "apache.commons.digester3.example.pojo.Bar");

            //5、开始解析
            Foo foo = digester.parse(Main.class.getClassLoader().getResourceAsStream("example.xml"));

            //6、打印解析结果
            System.out.println(foo.getName());
            for (Bar bar : foo.getBarList())
            {
                System.out.println(bar.getId() + "," + bar.getTitle());
            }

        }
        catch (IOException e)
        {

            e.printStackTrace();
        }
        catch (SAXException e)
        {

            e.printStackTrace();
        }
    }
}

 结果打印:

The Parent
123,The First Child
456,The Second Child
789,The Second Child

 注意以上代码涉及类型的自动转换,如id属性,由字符串类型转为整型,这里所有的类型转换都是由commons-beanutils包的ConvertUtils来完成的。

Digester属性配置

 org.apache.commons.digester3.Digester实例对象包含若干成员属性,这些属性值是可以设置的,以便我们自定义解析操作;

 为了让这些配置在XML解析前生效,这些属性值的更改一定要在parse方法调用之前设置;下面是一些可以配置的属性:

 另外,我们可以通过Digester的register方法,让Digester在遇到DOCTYPE声明时,使用本地dtd,而不是从网上获取,如下所示:

URL url = new URL("/org/apache/struts/resources/struts-config_1_0.dtd");
digester.register("-//Apache Software Foundation//DTD Struts Configuration 1.0//EN", url.toString());

Digester对象栈

 Digester使用的一个核心技术就是动态构建一颗java对象树,在构建的过程中,一个重要的辅助数据结构即对象栈;

 以如下xml为例:

<foo name="The Parent">
    <bar id="123" title="The First Child" />
    <bar id="456" title="The Second Child" />
    <bar id="789" title="The Second Child" />
</foo>

 在解析的时候,首先会创建一个foo对象,并压入对象栈,然后设置foo属性值name,紧接着,创建bar对象并压入栈,然后设置bar的属性值,然后将该bar对象添加的到foo对象的barlist属性集合中,然后bar对象弹出对象栈;

 以此类推,遇到起始标记的元素创建对象入栈,遇到结尾标记的元素做出栈操作,出栈前,需要将出栈对象并关联到上一个栈顶对象;

 最终,解析完xml后,留在栈顶的就关联了所有在xml解析中创建的动态对象了;

 Digester暴露出的与对象栈操作API如下所示:

Digester元素匹配模式

 Digester的一个关键特性是可以自动识别xml的层次结构,程序员只需要关心遇到匹配到某个元素后需要做哪些操作即可;

 如下是一个示例,其中a, a/b, a/b/c为匹配模式,对应xml中特定位置的元素:

<a>         -- Matches pattern "a"
    <b>       -- Matches pattern "a/b"
      <c/>    -- Matches pattern "a/b/c"
      <c/>    -- Matches pattern "a/b/c"
    </b>
    <b>       -- Matches pattern "a/b"
      <c/>    -- Matches pattern "a/b/c"
      <c/>    -- Matches pattern "a/b/c"
      <c/>    -- Matches pattern "a/b/c"
    </b>
  </a>

Digester规则处理

 当匹配到模式时,会触发规则处理,具体的规则处理机制是由这个org.apache.commons.digester3.Rule接口封装的,该接口定义了以下几个方法:

 默认情况下,Digester提供了以下Rule接口的实现类,我们在编码的时候可以直接使用,详见API文档:

 如下是一个SetNextRule规则实现类的示例(两种写法):

Rule rule = new SetNextRule("addBar",Bar.class);
digester.addRule("foo/bar", rule );

//digester.addSetNext("foo/bar", "addBar", Bar.class.getName());

Digester日志

 日志是调试、排查错误非常关键的一个环节,Digester记录了非常详细的日志,我们可以按如下方式来开启日志打印功能,这里的日志实现选择log4j。

        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
### set log levels ###
log4j.rootLogger = debug, stdout

### \u8F93\u51FA\u5230\u63A7\u5236\u53F0 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
2017-06-04 18:26:33  [ main:51 ] - [ DEBUG ]    Fire body() for SetPropertiesRule[aliases={}, ignoreMissingProperty=true]
2017-06-04 18:26:33  [ main:51 ] - [ DEBUG ]    Popping body text ''
2017-06-04 18:26:33  [ main:51 ] - [ DEBUG ]    Fire end() for SetPropertiesRule[aliases={}, ignoreMissingProperty=true]
2017-06-04 18:26:33  [ main:52 ] - [ DEBUG ]    Fire end() for ObjectCreateRule[className=apache.commons.digester3.example.pojo.Foo, attributeName=null]
2017-06-04 18:26:33  [ main:52 ] - [ DEBUG ]  [ObjectCreateRule]{foo} Pop 'apache.commons.digester3.example.pojo.Foo'
2017-06-04 18:26:33  [ main:52 ] - [ DEBUG ]  endDocument()
The Parent
123,The First Child
456,The Second Child
789,The Second Child

Digester例子

 前面我们已经举了一个Digester的简单使用例子,这里将继续展示几个示例,解析xml元素body值。
 如下XML文档就是我们要解析内容:

<web-app>
    <servlet>
        <servlet-name>action</servlet-name>
        <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
            <init-param>
                <param-name>application</param-name>
                <param-value>org.apache.struts.example.ApplicationResources</param-value>
            </init-param>
            <init-param>
                <param-name>config</param-name>
                <param-value>/WEB-INF/struts-config.xml</param-value>
            </init-param>
    </servlet>
</web-app>
/*
 * File Name: ServletBean.java
 * Description: 
 * Author: http://www.cnblogs.com/chenpi/
 * Create Date: 2017年6月4日
 */
package apache.commons.digester3.example.pojo;

import java.util.HashMap;
import java.util.Map;

/**
 * 
 * @author    http://www.cnblogs.com/chenpi/
 * @version   2017年6月4日
 */

public class ServletBean
{

    private String servletName;
    private String servletClass;
    private Map<String, String> initParams = new HashMap<String, String>();
    
    
    public void addInitParam(String paramName, String paramValue){
        initParams.put(paramName, paramValue);
    }
    /**
     * @return the servletName
     */
    public String getServletName()
    {
        return servletName;
    }
    /**
     * @param servletName the servletName to set
     */
    public void setServletName(String servletName)
    {
        this.servletName = servletName;
    }
    /**
     * @return the servletClass
     */
    public String getServletClass()
    {
        return servletClass;
    }
    /**
     * @param servletClass the servletClass to set
     */
    public void setServletClass(String servletClass)
    {
        this.servletClass = servletClass;
    }
    /**
     * @return the initParams
     */
    public Map<String, String> getInitParams()
    {
        return initParams;
    }
    /**
     * @param initParams the initParams to set
     */
    public void setInitParams(Map<String, String> initParams)
    {
        this.initParams = initParams;
    }
}
/*
 * File Name: Main2.java
 * Description: 
 * Author: http://www.cnblogs.com/chenpi/
 * Create Date: 2017年6月4日
 */
package apache.commons.digester3.example.simpletest;

import java.io.IOException;

import org.apache.commons.digester3.Digester;
import org.apache.commons.digester3.Rule;
import org.apache.commons.digester3.SetNextRule;
import org.xml.sax.SAXException;

import apache.commons.digester3.example.pojo.Bar;
import apache.commons.digester3.example.pojo.Foo;
import apache.commons.digester3.example.pojo.ServletBean;

/**
 * 
 * @author http://www.cnblogs.com/chenpi/
 * @version 2017年6月4日
 */

public class WebMain
{

    public static void main(String[] args)
    {
        try
        {
            // 1、创建Digester对象实例
            Digester digester = new Digester();

            // 2、配置属性值
            digester.setValidating(false);

            // 3、push对象到对象栈

            // 4、设置匹配模式、规则
            digester.addObjectCreate("web-app/servlet", "apache.commons.digester3.example.pojo.ServletBean");
            digester.addCallMethod("web-app/servlet/servlet-name", "setServletName", 0);
            digester.addCallMethod("web-app/servlet/servlet-class", "setServletClass", 0);
            digester.addCallMethod("web-app/servlet/init-param", "addInitParam", 2);
            digester.addCallParam("web-app/servlet/init-param/param-name", 0);
            digester.addCallParam("web-app/servlet/init-param/param-value", 1);

            // 5、开始解析
            ServletBean servletBean = digester
                .parse(ExampleMain.class.getClassLoader().getResourceAsStream("web.xml"));

            // 6、打印解析结果
            System.out.println(servletBean.getServletName());
            System.out.println(servletBean.getServletClass());
            for(String key : servletBean.getInitParams().keySet()){
                System.out.println(key + ": " + servletBean.getInitParams().get(key));
            }

        }
        catch (IOException e)
        {

            e.printStackTrace();
        }
        catch (SAXException e)
        {

            e.printStackTrace();
        }

    }
}
action
org.apache.struts.action.ActionServlet
application: org.apache.struts.example.ApplicationResources
config: /WEB-INF/struts-config.xml

参考资料

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

示例代码

https://github.com/peterchenhdu/apache-commons-digester-example

参考文章

Apache Commons Digester 一 (基础内容、核心API)
Apache Commons Digester 二(规则模块绑定-RulesModule、异步解析-asyncParse、xml变量Substitutor、带参构造方法)
Apache Commons Digester 三(规则注解)
tomcat源码解析(三)——Digester类源码解析及Rule分析
tomcat源码解读一 Digester的解析方式

上一篇 下一篇

猜你喜欢

热点阅读