简单了解Apache Commons Digester

Apache Commons Digester

Tomcat,Spring等系统框架中,都会出现xml配置,在最初的Spring框架中,都是通过xml文件描述对象信息继而注入到系统中,那xml是如何转化为JavaBean的呢?

Digester是来自Apache Commons包中的一个项目,最开始发现它是在Tomcat的源代码中,它主要用来完成以“事件驱动”的方式来处理XML元素,使用它能够很方便的将xmlJavaBean映射出来

Demo

下面的例子来自Digester官方文档

  • Maven引入需要依赖的包
<dependency>
      <groupId>commons-digester</groupId>
      <artifactId>commons-digester</artifactId>
      <version>1.8</version>
</dependency>
  • 新建一个example.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>
  • 新建对应的BarFoo
@Getter
@Setter
public class Bar
{

    private int id;
    private String title;
}
@Getter
@Setter
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();
    }

}

@Getter和@Setter 来自lombok插件

  • 配置Digester使用Digester
 public static void main(String[] agrs) throws Exception {
        try {
            //1、创建Digester对象实例
            Digester digester = new Digester();

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

            //3、push对象到对象栈
            //digester.push(new Foo());

            //4、设置匹配模式、规则
            digester.addObjectCreate("foo", "com.dengchengchao.digester.Foo");
            digester.addSetProperties("foo");
            digester.addObjectCreate("foo/bar", "com.dengchengchao.digester.Bar");
            digester.addSetProperties("foo/bar");
            digester.addSetNext("foo/bar", "addBar", "com.dengchengchao.digester.Bar");

            //5、开始解析
            Foo foo = (Foo) digester.parse(DigesterTest.class.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();
        }

    }
  • 输出结果
The Parent
123,The First Child
456,The Second Child
789,The Second Child

可以看到,FooBar的属性,通过简单的xml配置就能够修改属性等。

Digester详解

Digester最主要是通过来操作各个数据结构,在上面的例子中

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

Digester在解析的时候,首先会创建一个foo对象,并将对象压入栈中,然后设置栈顶对象foo属性nameThe Parent,紧接着创建Bar对象并压入栈,然后设置栈顶对象id123,title属性为The First Child,然后再遇到xml结束符时,弹出栈顶元素:bar,依次类推。

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

Digester元素匹配模式

Digester是以事件驱动方式处理xml文件的,它所具有的一个关键特性就是可以自动识别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 规则处理

当遇到匹配模式的时候,便会触发Digester对象定义的规则,简单介绍如下:

  • 创建类规则
    • addObjectCreate
    //当匹配到foo节点时,创建com.dengchengchao.digester.Foo对象,并压入栈顶
    digester.addObjectCreate("foo", "com.dengchengchao.digester.Foo");
    
    • addFactoryCreate
    //利用指定的工厂类创建一个对象,把对象压入栈。对于没有提供默认构造函数的类,这一规则很有用
    //所初始化的对象必须实现AbstractObjectCreationFactory接口
    digester.addFactoryCreate("tomcat-users/group",
                            new MemoryGroupCreationFactory(this), true);
    
  • 属性设置类规则
    • addSetProperties
      //给栈顶的对象设置相应节点的属性 
      //这将调用默认的Setter Getter
      digester.addSetProperties("foo");
      
    • addSetProperty
      //无论是Bean属性的名称,还是赋予该属性的值,都在当前XML元素中以属性的形式指定
      //例如:<article key="page" value="10" />
      digester.addSetProperties("foo");
      
    • BeanPropertySetterRule
      //把顶层Bean的指定名称的属性设置成当前XML元素包含的字符数据。
      //(通常用来处理<page>10</page>之类的结构)
      digester.addBeanPropertySetter("foo");
      
  • 父/子关系类规则
    • addSetRoot
      //调用栈底对象的一个方法,并把栈顶的对象作为参数传入。
      digester.addSetTop("foo/bar","addBar");
      
    • addSetTop
      //把栈里面上数第二的对象传递给顶层的对象。当子对象提供了一个setParenet方法时,这一规则很有用。
      digester.addSetTop("foo/bar","addBar");
      
    • addSetNext
      //弹出栈顶的对象,把它传递给紧接其下的另一个对象的指定名称的方法
      digester.addSetNext("foo/bar", "addBar", "com.dengchengchao.digester.Bar");
      
  • 方法类规则
    • addCallMethod

    • addCallParam

      //调用顶层Bean的指定名称的方法。被调用的方法可以有任意多个参数,参数的值通过后继的CallParamRule给出
      //表示方法调用的参数。参数的值或者取自指定名称的XML元素的属性,或者是当前元素包含的原始字符数据。这个规则要求用一个整数指定它在参数列表中的位置。
      digester.addCallMethod("web-app/servlet/init-param", "addInitParam", 2);
      digester.addCallParam("web-app/servlet/init-param/param-name", 0);
      

到这里,Digester的功能我们已经简单的了解,其实Digester能做的远不止这些,更多的比如:添加调试信息,异步解析,配置规则模块等,可以在官网上具体了解,这里不再赘述。

参考链接:

Apache Commons Digester

风一样的码农-博客园

Tomcat 7 启动分析- ITeye