Digester库



在前面的几个章节里,我们对tomcat里各个组件的配置完全是使用写硬编码的形式完成的。

如

Context context = new StandardContext();
Loader loader = new WebappLoader();
context.setLoader(loader);

就完成了向context容器里添加WepappLoader的功能。

这么做的问题就在于,一旦我想更改配置就必须得重新加载Bootstrap类。

幸运的是tomcat的设计者使用了一种给我灵活的配置方式,即使用xml来记录tomcat里各个组件的配置情况。并且使用Digester将xml中的元素转化为java对象。



Digester是Apache软件基金会Jakarta项目下的一个开源项目。更多具体的信息请问百度。

这里我们主要介绍Digester能干什么。

在apaceh官网上对这个项目的描述如下

XML-to-Java-object mapping utility.

看到了吧,就是咱们在上面说的把xml里面的元素转化为java类。

Digester类

先看一个xml 如下

<?xml version="1.0" encoding="ISO-8859-1"?>
<employee firstName="Freddie" lastName="Mercury">
  <office description="Headquarters">
    <address streetName="Wellington Avenue" streetNumber="223"/>
  </office>
</employee>

在这个xml里面,根元素是employee,里面包含一个元素office,office里面又包含一个元素address。

在讨论Digester如何解析xml前,我们先说两个概念,模式与规则。

模式:我无法给出一个书面的关于模式的定义。粗略的说在上面的xml中,employee元素的模式是employee;office元素的模式是employee/office;以此类推address元素的模式就是employee/office/address。大家应该大概明白了吧。说白了模式就是路径。

规则:是org.apache.commons.digester.Rule类的实例。规则指定了在分析xml时遇到某一规则应该执行的动作。说的够清楚了吧。另外Rule还有begin与end方法。挡在解析到匹配某个模式的元素的开始标签时会指向begin方法,end还用说吗?

例如在解析上面的xml时:

1 先解析到了employee元素,此时查找是否有对应的规则与此模式即employee匹配,若有,执行Rule对象的begin方法

2 遇到了office元素,此时查找是否有对应的规则与此模式即employee/office匹配,若有,执行Rule对象的begin方法

3 遇到了address元素的开始标签,此时查找是否有对应的规则与此模式即employee/office/address匹配,若有,执行Rule对象的begin方法

4 遇到address元素的结束标签,调用想匹配规则对象的end方法。

5 6自己补全,我不写了。

Digester预定义的规则

Digester预定义的规则主要有以下几个。

创建对象

创建对象一共有四种方式

最主要的两个例子如下:

digester.addObjectCreate("employee",ex15.pyrmont.digestertest.Employee.class);

或者

digester.addObjectCreate("employee","ex15.pyrmont.digestertest.Employee");

第一个参数是模式名,第二个参数可以是类名(String类型),也可以使Class。

还有两种方式就是在xml中写明要加载的类。

若xml中如下

<employee firstName="Brian" lastName="May"

className="ex15.pyrmont.digestertest.Employee">

代码写成下面的样子

digester.addObjectCreate("employee","ex15.pyrmont.digestertest.Employee", "className");

employee是类名,程序会按照上面方法的第三个参数去employee元素里找对应的属性值作为要加载的类,当然如果找不到那个属性的话,就是有上面方法的第二个参数来加载。同样第二个参数可以使String类型的类名也可以使Class。

(employee,office,address都在文章的最后)

设置属性

digester.addSetProperties("employee");

上面这句代码做的事情就是:系统找到匹配employee模式的元素后,如果那个元素有属性,就将相应的属性值注入到类里面去。

<employee firstName="Brian" lastName="May">

如果xml中如上,里面在它所匹配的那个类里面就至少得有setFirstName,setLastName两个方法。

调用方法

digester.addCallMethod("employee", "printName");

上面代码的功能就是找到匹配employee模式的元素后(准确的说是遇到结束标签后),就调用最先创建的那个类的printName方法。

创建对象之间的联系

digester.addSetNext("employee/office", "addOffice");

第一个参数的模式应该是这种形式的

firstobject/secondobjcet

上面的代码的意思就是调用firstobject的addOffice方法,并且以secondobject为参数。当然这两个object前面都已经产生好了。



下面这个例子让大家熟悉一下Digester的用法

示例代码1

package ex15.pyrmont.digestertest;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import org.apache.commons.digester.Digester;

public class Test02 {

  public static void main(String[] args) {
    String path = System.getProperty("user.dir") + File.separator +
            "src"+File.separator  + "etc";
    File file = new File(path, "employee2.xml");
    Digester digester = new Digester();
    // add rules
    digester.addObjectCreate("employee", "ex15.pyrmont.digestertest.Employee");
    digester.addSetProperties("employee");
    digester.addCallMethod("employee", "printName");

    digester.addObjectCreate("employee/office", "ex15.pyrmont.digestertest.Office");
    digester.addSetProperties("employee/office");

    digester.addSetNext("employee/office", "addOffice");

    digester.addObjectCreate("employee/office/address", "ex15.pyrmont.digestertest.Address");
    digester.addSetProperties("employee/office/address");
    digester.addSetNext("employee/office/address", "setAddress");
    try {
      Employee employee = (Employee) digester.parse(file);
      ArrayList<?> offices = employee.getOffices();
      Iterator<?> iterator = offices.iterator();
      System.out.println("-------------------------------------------------");
      while (iterator.hasNext()) {
        Office office = (Office) iterator.next();
        Address address = office.getAddress();
        System.out.println(office.getDescription());
        System.out.println("Address : " +
          address.getStreetNumber() + " " + address.getStreetName());
        System.out.println("--------------------------------");
      }

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

employee2.xml如下

<?xml version="1.0" encoding="ISO-8859-1"?>
<employee firstName="Freddie" lastName="Mercury">
  <office description="Headquarters">
    <address streetName="Wellington Avenue" streetNumber="223"/>
  </office>
  <office description="Client site">
    <address streetName="Downing Street" streetNumber="10"/>
  </office>
</employee>

至于最后的运行结果大家自己猜一下,然后运行一下看看。

Rule类

rule,顾名思义就是规则,就是当解析器解析到某个模式时要干什么事。

可能大家要问,不是已经有前面的addObjectCreate了么?问题是如果我想让程序每解析出一个"类"就做个什么事怎么办?创建一个类只是各种规则的一种而已。

org.apache.commons.digester.rule里面有两个方法比较重要,分别是begin与end。

 public void begin(Attributes attributes) throws Exception

 public void end() throws Exception

当在解析到某种模式的是时候,遇到开始标签就调用begin方法,结束标签就调用end方法。



现在我们说说之前的addObjectCreate方法。

   Digester.java
    protected Rules rules = null;

    public void addObjectCreate(String pattern, Class clazz) {
        addRule(pattern,new ObjectCreateRule(clazz));
    }
    public void addRule(String pattern, Rule rule) {
        rule.setDigester(this);
        getRules().add(pattern, rule); //getRules返回的就是Digester中的那个rules

    }

    ObjectCreateRule.java
     public void begin(Attributes attributes) throws Exception {
          ....
        // Instantiate the new object and push it on the context stack
        Class clazz = digester.getClassLoader().loadClass(realClassName);
        Object instance = clazz.newInstance();
        digester.push(instance);
    }

Digester里面存储了一个rules对象

    rules是一个接口 我们使用的是它的实现类RulesBase

    在RulesBase中有下面的代码

    protected ArrayList rules = new ArrayList();

另外ObjectCreateRule也是rule的子类。

换言之,包括addObjectCreate在内前面说的几个预定义规则干的事情就是在Digester的rules里面添加一个rule而已。

等到扫描器扫描到指定的模式时,就执行指定的rule里的begin,end。

RuleSet

这是什么东西?

大家看代码就懂了。

package ex15.pyrmont.digestertest;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import org.apache.commons.digester.Digester;

public class Test03 {

  public static void main(String[] args) {
    String path = System.getProperty("user.dir") + File.separator +
                "src"+File.separator  + "etc";
    File file = new File(path, "employee2.xml");
    Digester digester = new Digester();
    digester.addRuleSet(new EmployeeRuleSet());  //EmployeeRuleSet是什么东西?
    try {
      Employee employee = (Employee) digester.parse(file);

      ...... 与Test2一样
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
}
package ex15.pyrmont.digestertest;

import org.apache.commons.digester.Digester;
import org.apache.commons.digester.RuleSetBase;

//一定要继承RuleSetBase
public class EmployeeRuleSet extends RuleSetBase  {

  //复写    addRuleInstances
  public void addRuleInstances(Digester digester) {
    // add rules
    digester.addObjectCreate("employee", "ex15.pyrmont.digestertest.Employee");
    digester.addSetProperties("employee");  

    digester.addObjectCreate("employee/office", "ex15.pyrmont.digestertest.Office");
    digester.addSetProperties("employee/office");

    digester.addSetNext("employee/office", "addOffice");

    digester.addObjectCreate("employee/office/address","ex15.pyrmont.digestertest.Address");
    digester.addSetProperties("employee/office/address");
    digester.addSetNext("employee/office/address", "setAddress");
  }
}

RuleSetBase就是规则的集合嘛。

Test03与Test02的结果一样,但是test3的代码看起来更少一些,就是因为我们把规则都隐藏在了EmployeeRuleSet里面了。

相关代码

package ex15.pyrmont.digestertest;

import java.util.ArrayList;

public class Employee {
  private String firstName;
  private String lastName;
  private ArrayList<Office> offices = new ArrayList<Office>();

  public Employee() {
    System.out.println("Creating Employee");
  }
  public String getFirstName() {
    return firstName;
  }
  public void setFirstName(String firstName) {
    System.out.println("Setting firstName : " + firstName);
    this.firstName = firstName;
  }
  public String getLastName() {
    return lastName;
  }
  public void setLastName(String lastName) {
    System.out.println("Setting lastName : " + lastName);
    this.lastName = lastName;
  }
  public void addOffice(Office office) {
    System.out.println("Adding Office to this employee");
    offices.add(office);
  }
  public ArrayList<Office> getOffices() {
    return offices;
  }
  public void printName() {
    System.out.println("My name is " + firstName + " " + lastName+"sssssssss");
  }
}

package ex15.pyrmont.digestertest;

public class Office {
  private Address address;
  private String description;
  public Office() {
    System.out.println("..Creating Office");
  }
  public String getDescription() {
    return description;
  }
  public void setDescription(String description) {
    System.out.println("..Setting office description : " + description);
    this.description = description;
  }
  public Address getAddress() {
    return address;
  }
  public void setAddress(Address address) {
    System.out.println("..Setting office address : " + address);
    this.address = address;
  }
}
package ex15.pyrmont.digestertest;

public class Address {
  private String streetName;
  private String streetNumber;
  public Address() {
    System.out.println("....Creating Address");
  }
  public String getStreetName() {
    return streetName;
  }
  public void setStreetName(String streetName) {
    System.out.println("....Setting streetName : " + streetName);
    this.streetName = streetName;
  }
  public String getStreetNumber() {
    return streetNumber;
  }
  public void setStreetNumber(String streetNumber) {
    System.out.println("....Setting streetNumber : " + streetNumber);
    this.streetNumber = streetNumber;
  }
  public String toString() {
    return "...." + streetNumber + " " + streetName;
  }
}

How tomcat works 读书笔记十五 Digester库 上的更多相关文章

  1. How tomcat works 读书笔记十五 Digester库 下

    在这一节里我们说说ContextConfig这个类. 这个类在很早的时候我们就已经使用了(之前那个叫SimpleContextConfig),但是在之前它干的事情都很简单,就是吧context里的co ...

  2. How tomcat works 读书笔记十四 服务器组件和服务组件

    之前的项目还是有些问题的,例如 1 只能有一个连接器,只能处理http请求,无法添加另外一个连接器用来处理https. 2 对容器的关闭只能是粗暴的关闭Bootstrap. 服务器组件 org.apa ...

  3. How tomcat works 读书笔记十二 StandardContext 下

    对重载的支持 tomcat里容器对重载功能的支持是依靠Load的(在目前就是WebLoader).当在绑定载入器的容器时 public void setContainer(Container cont ...

  4. How tomcat works 读书笔记十二 StandardContext 上

    在tomcat4中,StandardContext.java是最大的一个类,有117k.废话不说,开始分析吧. 其实要分析StandardContext,也就主要分析两个方法,一个start,一个in ...

  5. How Tomcat Works 读书笔记 八 加载器 上

    Java的类加载器 具体资料见 http://blog.csdn.net/dlf123321/article/details/39957175 http://blog.csdn.net/dlf1233 ...

  6. how tomcat works 读书笔记(二)----------一个简单的servlet容器

    app1 (建议读者在看本章之前,先看how tomcat works 读书笔记(一)----------一个简单的web服务器 http://blog.csdn.net/dlf123321/arti ...

  7. how tomcat works 读书笔记四 tomcat的默认连接器

    事实上在第三章,就已经有了连接器的样子了,只是那仅仅是一个学习工具,在这一章我们会開始分析tomcat4里面的默认连接器. 连接器 Tomcat连接器必须满足下面几个要求 1 实现org.apache ...

  8. how tomcat works读书笔记 七 日志记录器

    大家可以松一口气了,这个组件比较简单,这一节和前面几节想比,也简单的多. Logger接口 Tomcat中的日志记录器都必须实现org.apache.catalina.Logger接口. packag ...

  9. How Tomcat Works读书笔记三-------连接器

    几个概念 HttpServlet,Servlet Servlet是一个接口,定义了一种网络服务,我们所有的servlet都要实现它(或它的子类) HttpServlet是一个抽象类,它针对的就是htt ...

随机推荐

  1. Xcode8出现的一些常见问题

    消除无用输出语句问题:Xcode8之后,新创建的项目在手机上运行后,就会在输出窗口,输出一大堆乱七八糟的日志,对我们几乎没有用,如图: 解决办法: [product]-[scheme]-[Edit S ...

  2. android view事件分发机制

    首先我们先写个简单的例子来测试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个MyButton继承Button,然后把跟事件传播有关的方法进行复写,然后添加上日志 ...

  3. Android JavascriptBridge 详解(二)

    原文出自:http://blog.csdn.net/sk719887916/article/details/47189607 Android开发目前现状来说,开发者大部分时间花在UI的屏幕适配上,使用 ...

  4. GraphX PageRank

    GraphX算法模型:PageRank 一:算法介绍         PageRank是Google专有的算法,用于衡量特定网页相对于搜索引擎索引中的其他网页而言的重要程度. 一个页面的"得 ...

  5. 【问题汇总】ScrollView嵌套GridView的问题

    在开发中遇到了ScrollView嵌套GridView的情况,由于这两款控件都自带滚动条,当它们碰到一起的时候便会出问题,即GridView会显示不全. 解决办法,自定义一个GridView控件. [ ...

  6. Dynamics CRM 开启EmailRouter日志记录

    找到mailrouter的安装路径,在service文件夹下找到"Microsoft.Crm.Tools.EmailAgent.xml"这个文件,已管理员方式打开,找到loglev ...

  7. 【一天一道LeetCode】#225. Implement Stack using Queues

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Impleme ...

  8. JDK 7中的文件操作的新特性

    文件系统综述 一个文件系统在某种媒介(通常是一个或多个硬盘)上存储和组织文件.如今的大多数文件系统都是以树状结构来存储文件.在树的顶端是一个或多个根节点,在根节点一下,是文件和目录(在Windows系 ...

  9. Unable to instantiate Action, MenuAction, defined for 'QueryMenuAll' in namespace '/'MenuAction

    我刚好也遇到这样的情况,发现是自己的配置文件里写错了,spring里的id属性值要对应struts里class属性值.

  10. 【java虚拟机系列】java中类与对象的加载顺序

    首先了解一下Java虚拟机初始化的原理. JVM通过加装.连接和初始化一个Java类型,使该类型可以被正在运行的Java程序所使用.类型的生命周期如下图所示: 装载和连接必须在初始化之前就要完成. 类 ...