Tomcat相关技术-Digester(二)Digester使用和原理

1 概述

  • Digester是一款用于将xml转换为Java对象的事件驱动型工具,是对SAX的高层次的封装。Digester相对于SAX提供了更加友好的接口,隐藏了xml节点具体层次的细节,让开发者能加载专注于处理过程。

  • Digester最早是Web框架Apache Struts的一部分,后来由于其通用性移植到了Apache Common项目中。

2 Digester解决了SAX哪些问题

SAX基于事件解析Xml,最重要的就是要编写一个解析器类。

public class SaxHandler extends DefaultHandler {

     /**
     * 解析一个xml标签字符时触发回调
       (该触发回调发生在startElement之后endElement之前)
     * @param ch  xml文档完整字符数组
     * @param start   当前标签中字符在ch开始位置
     * @param length  当前标签中字符的长度
     * @throws SAXException
     */
    @Override
    public void characters(char[] ch, int start, int length){
        ..省略实现
    }
    
    
     /**
     * 解析一个xml标签结束时触发回调
     * @param uri
     * @param localName
     * @param name        当前标签的名称
     * @throws SAXException
     */
    @Override
    public void endElement(String uri, String localName, String name){
        ..省略实现
    }
    
    
    /**
     * 解析一个xml标签开始时触发回调
     * @param uri
     * @param localName
     * @param name        当前标签的名称
     * @param attributes  标签中的属性对象
     * @throws SAXException
     */
    @Override
    public void startElement(String uri, String localName, String name,
                             Attributes attributes) throws SAXException {
        ..省略实现
    }
                             
    /**
     * 解析一个xml文件开始时回调
     */
    @Override
    public void startDocument() throws SAXException{
        ..省略实现
    }

    /**
     * 解析一个xml文件结束时回调
     */
    @Override
    public void endDocument() throws SAXException{
        ..省略实现
    }

2.1 SAX缺陷

  • 1 如果解析XML上不同标签节点存在依赖关系时,需要开发者自行维护依赖关系,说具体就是如果需要子标签对象手动设置到父标签属性中,子标签需要程序开发者手动保存下来。

  • 2 不同xml的标签属性,字符都不同,而SAX只提供了所有标签通用回调事件,不能针对不同标签定制。

3 Digester设计

image
  • 1 Digester继承DefaultHandler表明Digester是对SAX的扩展实现

  • 2 Digester内部存类型为Rules的属性,Rules为了一个其实现类为RulesBase,RulesBase内部维护HashMap,其中key对应匹配xml规则的字符串,value表示表示针对此xml规则解析规则列表。每个解析规则使用Rule类来表示。

public class Digester extends DefaultHandler2 {
       ...省略代码
       protected Rules rules = null;
       ...省略代码
}

public class RulesBase implements Rules {
    ...省略代码
    //String     表示匹配xml匹配规则(可以用正则表达式)
    //List 表示针对对应xml规则解析规则列表
    protected HashMap> cache = new HashMap<>();
    ...省略代码

  • 3 同时Digester内部维护了一个栈数据结构,用来处理当前解析的xml标签节点,解析前会将xml标签对象入栈(push),解析后会将xml标签对象出栈(pop),栈最顶部的对象永远都是现在正在解析的对象。这样就可以将有父子关系的节点对象在栈中保存下来。

4 Rule规则对应接口方法

 begin():当读取到匹配节点的开始部分时调用,会将该节点的所有属性作为从参数传入。

 body():当读取到匹配节点的内容时调用,注意指的不是子节点,而是嵌入内容为普通文本。

 end():当读取到匹配节点的结束部分时调用,如果存在子节点,只有当子节点处理完毕后该方法才会被调用。

 finish():当整个parse()方法完成时调用,多用于清楚临时数据和缓存数据

可以发现这和DefaultHandler方式很像,不同的时规则只针对特定的某个xml标签规则

5 xml匹配规则

[图片上传失败...(image-4163f4-1565013353761)]

例子

              
              
          
    
    
      
      
      
    
    
  
a匹配标签 
a/b匹配
a/b/c匹配
*/b 匹配

6 Digester常用API

public void setValidating(boolean validating) // 是否根据DTD校验XML

public void push(Object object) // 将对象压入栈

public Object peek() // 获取栈顶对象

public Object pop() // 弹出栈顶对象

public Object parse(InputSource input) // 解析输入源

public void addRule(String pattern, Rule rule) //针对指定xml标签设置规则解析器


7 给指定标签设置规则

1 创建一个定义规则类,该类需要继承Rule

2 调用digester.addRule API 函数

public class ConnectorCreateRule extends Rule {
 
  @Override
    public void begin(String namespace, String name, Attributes attributes){
      //do something
    }
  
  @Override
    public void begin(String namespace, String name, Attributes attributes){
      //do something
    }
}

//表示匹配到结构的标签时,使用创建的自定义接口
digester.addRule("Server/Service/Connector",new ConnectorCreateRule());

为单个标签添加规则组(多个标签规则)

当需要某个xml规则添加多个规则时可以使用RuleSet

1 创建一个RuleSet实现类,该类需要继承RuleSetBase

2 构造方法中需要定义xml规则

3 实现addRuleInstances方法对传入的规则添加规则

public class MyRuleSet
  extends RuleSetBase {

  public MyRuleSet()
  {
    this("");
  }

  public MyRuleSet(String prefix)
  {
    super();
    this.prefix = prefix;
    this.namespaceURI = "http://www.mycompany.com/MyNamespace";
  }

  protected String prefix = null;

  public void addRuleInstances(Digester digester)
  {
    digester.addObjectCreate( prefix + "foo/bar",
      "com.mycompany.MyFoo" );
    digester.addSetProperties( prefix + "foo/bar" );
  }

}

digester.addRuleSet( new MyRuleSet( "baz/" ) );

内置的规则接口

Digester内置了一些规则,可以调用使用DigesterAPI 直接调用,给指定xml添加内置规则。

ObjectCreateRule: 当begin()方法调用时,该规则会将指定的Java类实例化,并将其放入对象栈。具体的Java类可由该规则在构造方法出啊如,也可以通过当前处理XML节点的某个属性指定,属性名称通过构造方法传入。当end()方法调用时,该规则创建的对象将从栈中取出。

FactoryCreateRule:ObJectCreateRule规则的一个变体,用于处理Java类无默认构造方法的情况,或者需要在Digester处理该对象之前执行某些操作的情况。

SetPropertiesRule:当begin()方法调用时,Digester使用标准的Java Bean属性操作方法(setter)将当前XML节点的属性值设置到对象栈顶部的对象中。

SetPropertyRule:当begin()方法调用时,Digester会设置栈顶部对象指定属性的值,其中属性名和属性值分别通过XML节点的两个属性指定。

SetNextRule:当end()方法调用时,Digester会找到位于栈顶部对象的下一个对象,并调用其指定的方法,同时将栈顶部对象作为参数传入,用于设置父对象的子对象,以便在栈对象之间建立父子关系,从而形成对象树,方便引用。

SetTopRule:与setNextRule对象,当end()方法调用时,Digester会找到位于站顶部的对象,调用其指定方法,同时将位于顶部下一个对象作为参数传入,用于设置当前对象的父对象。

CallMethRule:该规则用于在end()方法调用时执行栈顶对象的某个方法,参数值由CallParamRule获取。

CallParamRule:该规则与CallMethodRule配合使用,作为其子节点的处理规则创建方法参数,参数值可取自某个特殊属性,也可以取自节点的内容。
NodeCreateRule:用于将XML文档树的一部分转换为DOM节点,并放入栈。

DEMO






    director
    joke



package com.wuhao.web.tomcat.digester;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Department {

    private String name;

    private String code;

    private Map extension = new HashMap();

    private List users = new ArrayList();

    public void addUser(User user){
        this.users.add(user);
    }

    public void putExtension(String name,String value){
        this.extension.put(name,value);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public Map getExtension() {
        return extension;
    }

    public void setExtension(Map extension) {
        this.extension = extension;
    }

    public List getUsers() {
        return users;
    }

    public void setUsers(List users) {
        this.users = users;
    }
}

package com.wuhao.web.tomcat.digester;

public class User {
    private String name;
    private String code;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

package com.wuhao.web.tomcat.digester;

import org.apache.tomcat.util.digester.Digester;

import java.io.File;
import java.net.URL;

public class DigesterRule {

    public Department execute(String filePath) throws Exception {
        Digester digester = new Digester();
        digester.setValidating(false);
        digester.setRulesValidation(true);

        // addObjectCreate方法的意思是碰到xml文件中的department节点则创建一个Department对象
        digester.addObjectCreate("department", "com.wuhao.web.tomcat.digester.Department");
        // addSetProperties方法的意思是根据department节点中的属性信息调用相应属性的setter方法
        digester.addSetProperties("department");
        // addObjectCreate方法的意思是碰到xml文件中的department节点则创建一个Department对象
        digester.addObjectCreate("department/user", "com.wuhao.web.tomcat.digester.User");
        digester.addSetProperties("department/user");

        digester.addSetNext("department/user", "addUser", "com.wuhao.web.tomcat.digester.User");
        digester.addCallMethod("department/extension", "putExtension", 2);
        digester.addCallParam("department/extension/property-name", 0);
        digester.addCallParam("department/extension/property-value", 1);
        URL url = this.getClass().getClassLoader().getResource(filePath);
        return (Department) digester.parse(new File(url.getFile()));
    }
}

public class Test {

    @org.junit.Test
    public void testJavaRule() throws Exception {
        Department department = new DigesterRule().execute("tomcat/department.xml");
        System.out.println(department);
    }
}

你可能感兴趣的:(Tomcat相关技术-Digester(二)Digester使用和原理)