(四)Digester通过server.xml文件构建server对象

  真正的server.xml文件中包含很多内容,但只要连接其规则,就能从server.xml文件中解读出server对象的内容。
  从使用示例中分析.

代码

  源码地址
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>

对象类:

public class Employee {
    private String firstName;
    private String lastName;
    private ArrayList offices = new ArrayList();
    
    public Employee() {
        System.out.println("Creating Employee");
    }

    //TODO getter setter 方法

    public void printName() {
        System.out.println("My name is " + firstName + " " + lastName);
    }
}

public class Office {
    private Address address;
    private String description;
    
    public Office() {
        System.out.println("..Creating Office");
    }

    //TODO getter setter  方法
}

public class Address {
    private String streetName;
    private String streetNumber;

    public Address() {
        System.out.println("....Creating Address");
    }
    
    // getter setter 方法
    
    public String toString() {
        return "...." + streetNumber + " " + streetName;
    }
}

测试类

public class Test02 {

    public static void main(String[] args) {
        String path = System.getProperty("user.dir") + File.separator +
                "/my-tomcat/src/main/resources/chapter15";
        File file = new File(path, "employee2.xml");
        Digester digester = new Digester();
        // add rules
        digester.addObjectCreate("employee", "com.tomcat.chapter15.digestertest.Employee");
        digester.addSetProperties("employee");
        digester.addObjectCreate("employee/office", "com.tomcat.chapter15.digestertest.Office");
        digester.addSetProperties("employee/office");
        digester.addSetNext("employee/office", "addOffice");
        digester.addObjectCreate("employee/office/address",
                "com.tomcat.chapter15.digestertest.Address");
        digester.addSetProperties("employee/office/address");
        digester.addSetNext("employee/office/address", "setAddress");

        parseAndTest(digester, file);

    }

    public static void parseAndTest(Digester digester, File file) {
        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();
        }
    }
}

输出结果

Creating Employee
Setting lastName : Mercury
Setting firstName : Freddie
..Creating Office
..Setting office description : Headquarters
....Creating Address
....Setting streetNumber : 223
....Setting streetName : Wellington Avenue
..Setting office address : ....223 Wellington Avenue
Adding Office to this employee
..Creating Office
..Setting office description : Client site
....Creating Address
....Setting streetNumber : 10
....Setting streetName : Downing Street
..Setting office address : ....10 Downing Street
Adding Office to this employee
###################################################
Headquarters
Address : 223 Wellington Avenue
--------------------------------------------------
Client site
Address : 10 Downing Street
--------------------------------------------------

归纳下使用步骤:

  1. 创建digester对象;
  2. 在digester中添加解析规则;
  3. 调用digester.parse(file), 就会返回对象。

  分析规则对应的操作

// 遇到employee标签,则创建"com.tomcat.chapter15.digestertest.Employee"对象
digester.addObjectCreate("employee", "com.tomcat.chapter15.digestertest.Employee"); 
// 为"com.tomcat.chapter15.digestertest.Employee"对象添加属性(<employee firstName="Freddie" lastName="Mercury"> 即将firstName和lastName的值赋给新建的对象)
digester.addSetProperties("employee");
// 调用新建对象的printName()方法
digester.addCallMethod("employee", "printName");
// 遇到employee下的office标签,则创建"com.tomcat.chapter15.digestertest.Office"对象
digester.addObjectCreate("employee/office", "com.tomcat.chapter15.digestertest.Office");
digester.addSetProperties("employee/office");
//这个是复制操作,将当前的对象通过addOffice方法赋给(简单理解的话就是外层对象)
digester.addSetNext("employee/office", "addOffice");
// ......

  通过上面的规则,就可以在遇到xml中不同标签时,将其变成我们想要的对象。

源码分析

  Digester类包含一个protected Rules rules = null; protected ArrayStack stack = new ArrayStack();

public class Digester extends DefaultHandler {

    //用来存储添加的各种规则
    protected Rules rules = null;
    
    //压入每次产生的新对象, 对于前面提到的addSetNext规则,就会用到这个堆栈
    protected ArrayStack stack = new ArrayStack();

    public Rules getRules() {
        if (this.rules == null) {
            this.rules = new RulesBase();
            this.rules.setDigester(this);
        }
        return (this.rules);
    }
    
    //...
    
    //所有的规则,都是一样,会调用addRule(...)加入到rules中;
    public void addObjectCreate(String pattern, String className) {
        addRule(pattern, new ObjectCreateRule(className));
    }
    
    public void addRule(String pattern, Rule rule) {
        rule.setDigester(this);
        getRules().add(pattern, rule);
    }
    
    //因为传入文件类型不同,有多个重载函数, 但流程都是大同小异的
    public Object parse(InputStream input) throws IOException, SAXException {
        configure();
        InputSource is = new InputSource(input);
        getXMLReader().parse(is);
        cleanup();
        return (root);
    }
    
}

  所有的规则,都会调用addRule()方法,将规则rule加入到RuleBase的map中
  解析流程:

  1. 配置自身类,initialize(), 实际误操作;

  2. 获取配置文件的字节流;set其systemId(不知道有撒用);

  3. 获取xmlReader(解析xml文件用的类)

  4. xml.parse(input…), 关键的一步,分析xml文件,涉及的内容(一层包一层),但需要知道:
    当解析到标签时,digester类会从ruleBase中匹配出XXX对应的一系列规则;然后依次执行rule的begin()函数, 并将XXX匹配的规则加入到一个使用记录堆栈,

    public void startElement(String namespaceURI, String localName,
                             String qName, Attributes list)
            throws SAXException {
        boolean debug = log.isDebugEnabled();
        
        //....
    
        // Fire "begin" events for all relevant rules
        //找出匹配规则
        List rules = getRules().match(namespaceURI, match);
        matches.push(rules);
        //顺序执行begin()函数
        if ((rules != null) && (rules.size() > 0)) {
            Substitutor substitutor = getSubstitutor();
            if (substitutor!= null) {
                list = substitutor.substitute(list);
            }
            for (int i = 0; i < rules.size(); i++) {
                try {
                    Rule rule = (Rule) rules.get(i);
                    if (debug) {
                        log.debug("  Fire begin() for " + rule);
                    }
                    rule.begin(namespaceURI, name, list);
                } catch (Exception e) {
                    log.error("Begin event threw exception", e);
                    throw createSAXException(e);
                } catch (Error e) {
                    log.error("Begin event threw error", e);
                    throw e;
                }
            }
        } else {
            if (debug) {
                log.debug("  No rules found matching '" + match + "'.");
            }
        }
    
    }
    

    当解析到标签时,从使用记录堆栈(匹配规则时加入的)弹出栈顶元素,因为标签肯定是成对出现的,然后倒序 执行rule的end()函数,

    public void endElement(String namespaceURI, String localName,
                           String qName) throws SAXException {
    
        //...
    
        // Fire "body" events for all relevant rules
        // 使用记录堆栈弹出对应的规则集
        List rules = (List) matches.pop();
        if ((rules != null) && (rules.size() > 0)) {
            String bodyText = this.bodyText.toString();
            Substitutor substitutor = getSubstitutor();
            if (substitutor!= null) {
                bodyText = substitutor.substitute(bodyText);
            }
            for (int i = 0; i < rules.size(); i++) {
                try {
                    Rule rule = (Rule) rules.get(i);
                    if (debug) {
                        log.debug("  Fire body() for " + rule);
                    }
                    rule.body(namespaceURI, name, bodyText);
                } catch (Exception e) {
                    log.error("Body event threw exception", e);
                    throw createSAXException(e);
                } catch (Error e) {
                    log.error("Body event threw error", e);
                    throw e;
                }
            }
        } else {
            if (debug) {
                log.debug("  No rules found matching '" + match + "'.");
            }
        }
    
        // Recover the body text from the surrounding element
        bodyText = (StringBuffer) bodyTexts.pop();
        if (debug) {
            log.debug("  Popping body text '" + bodyText.toString() + "'");
        }
    
        // Fire "end" events for all relevant rules in reverse order
        if (rules != null) {
            for (int i = 0; i < rules.size(); i++) {
                // 倒序执行的代码
                int j = (rules.size() - i) - 1;
                try {
                    Rule rule = (Rule) rules.get(j);
                    if (debug) {
                        log.debug("  Fire end() for " + rule);
                    }
                    rule.end(namespaceURI, name);
                } catch (Exception e) {
                    log.error("End event threw exception", e);
                    throw createSAXException(e);
                } catch (Error e) {
                    log.error("End event threw error", e);
                    throw e;
                }
            }
        }
    
        // Recover the previous match expression
        int slash = match.lastIndexOf('/');
        if (slash >= 0) {
            match = match.substring(0, slash);
        } else {
            match = "";
        }
    
    }
    

    例如ObjectCreateRule就对应加载所需类实例化并压入前面提到的堆栈

    public void begin(Attributes attributes) throws Exception {
        // Identify the name of the class to instantiate
        String realClassName = className;
        if (attributeName != null) {
            String value = attributes.getValue(attributeName);
            if (value != null) {
                realClassName = value;
            }
        }
        if (digester.log.isDebugEnabled()) {
            digester.log.debug("[ObjectCreateRule]{" + digester.match +
                    "}New " + realClassName);
        }
    
        // Instantiate the new object and push it on the context stack
        // 加载类
        Class clazz = digester.getClassLoader().loadClass(realClassName);
        // 实例化
        Object instance = clazz.newInstance();
        // 进栈
        digester.push(instance);
    
    }
    

    下面是ObjectCreateRule对应的end()函数, 也就是出栈。

    public void end() throws Exception {
        Object top = digester.pop();
        if (digester.log.isDebugEnabled()) {
            digester.log.debug("[ObjectCreateRule]{" + digester.match +
                    "} Pop " + top.getClass().getName());
        }
    }
    

    特别提下setNextRule,其begin()和end()如下:

    //begin函数使用的是其父类Rule的begin方法,什么都不操作
    
    public void end() throws Exception {
        // Identify the objects to be used
        //获取栈顶部的两个元素
        Object child = digester.peek(0);
        Object parent = digester.peek(1);
        if (digester.log.isDebugEnabled()) {
            if (parent == null) {
                digester.log.debug("[SetNextRule]{" + digester.match +
                        "} Call [NULL PARENT]." +
                        methodName + "(" + child + ")");
            } else {
                digester.log.debug("[SetNextRule]{" + digester.match +
                        "} Call " + parent.getClass().getName() + "." +
                        methodName + "(" + child + ")");
            }
        }
    
        // Call the specified method
        Class paramTypes[] = new Class[1];
        if (paramType != null) {
            paramTypes[0] =
                    digester.getClassLoader().loadClass(paramType);
        } else {
            paramTypes[0] = child.getClass();
        }
        //调用parent类的方法(addSetNextRule中声明的方法,将)将child对象设置进去
        if (useExactMatch) {
            MethodUtils.invokeExactMethod(parent, methodName,
                new Object[]{ child }, paramTypes);
        } else {
            MethodUtils.invokeMethod(parent, methodName,
                new Object[]{ child }, paramTypes);
        }
    }
    

    注意:setNextRule是不做出栈操作的,出栈操作是objectCreate的end()函数进行的。(这里也体现了为什么倒序执行匹配规则的end()函数)

RulesBase类:

  主要包含下面的方法:

public class RulesBase implements Rules {
    protected HashMap cache = new HashMap();
    protected ArrayList rules = new ArrayList();
    
    protected Digester digester = null;
    
    //...
    
    public void setDigester(Digester digester) {
        this.digester = digester;
        Iterator items = rules.iterator();
        while (items.hasNext()) {
            Rule item = (Rule) items.next();
            item.setDigester(digester);
        }
    }
    
    public void add(String pattern, Rule rule) {
        // to help users who accidently add '/' to the end of their patterns
        int patternLength = pattern.length();
        if (patternLength>1 && pattern.endsWith("/")) {
            pattern = pattern.substring(0, patternLength-1);
        }
        
        List list = (List) cache.get(pattern);
        if (list == null) {
            list = new ArrayList();
            cache.put(pattern, list);
        }
        list.add(rule);
        rules.add(rule);
        if (this.digester != null) {
            rule.setDigester(this.digester);
        }
        if (this.namespaceURI != null) {
            rule.setNamespaceURI(this.namespaceURI);
        }
    }
    
    
    public List match(String pattern) {
        return (match(null, pattern));
    }
    public List match(String namespaceURI, String pattern) {

        // List rulesList = (List) this.cache.get(pattern);
        List rulesList = lookup(namespaceURI, pattern);
        if ((rulesList == null) || (rulesList.size() < 1)) {
            // Find the longest key, ie more discriminant
            String longKey = "";
            Iterator keys = this.cache.keySet().iterator();
            while (keys.hasNext()) {
                String key = (String) keys.next();
                if (key.startsWith("*/")) {
                    if (pattern.equals(key.substring(2)) ||
                        pattern.endsWith(key.substring(1))) {
                        if (key.length() > longKey.length()) {
                            // rulesList = (List) this.cache.get(key);
                            rulesList = lookup(namespaceURI, key);
                            longKey = key;
                        }
                    }
                }
            }
        }
        if (rulesList == null) {
            rulesList = new ArrayList();
        }
        return (rulesList);
    }
    
}

  从代码可以看出RulesBase将所有规则依据xml中的标签分类,将属于同一标签的规则按添加先后顺序保存在list中, 并以标签为key放在map中。
  match()方法是将对应标签的list(规则)取出,而namespaceURI虽然看着繁琐,但其只是加了一层过滤,匹配出一些特定namespace的规则.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值