• 曹工说Tomcat2:自己撸一个简易Tomcat Digester


    一、前言

    框架代码其实也没那么难,大家不要看着源码就害怕,现在去看 Tomcat 3.0的代码,保证还是看得懂一半,照着撸一遍基本上很多问题都能搞定了。这次我们就模拟 Tomcat 中的 Digester(xml解析工具)来仿写一个相当简易的版本。上一篇说了如何利用 sax 模型来解析 xml,但是,该程序还有相当多的优化空间。这一篇,我们一起将程序进行一些优化。之前的版本有什么问题呢?请看:

     1     @Override
     2     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
     3         System.out.println("startElement: " + qName + " It's the " + eventOrderCounter.getAndIncrement() + " one");
     4 
     5         if ("Coder".equals(qName)) {
     6 
     7             Coder coder = new Coder();
     8 
     9             setProperties(attributes,coder);
    10 
    11             stack.push(coder);
    12         } else if ("Girl".equals(qName)) {
    13 
    14             Girl girl = new Girl();
    15             setProperties(attributes, girl);
    16 
    17             Coder coder = (Coder) stack.peek();
    18             coder.setGirl(girl);
    19         }
    20     }

     

    上图为当前xml handler的代码,注意第5-6行,我们这里写死了,当元素为 Coder 的时候,就生成 Coder类的对象。那要是,我现在不想生成 Coder 类了,就得修改这里的程序。这样还是太不灵活了,所以我们考虑将 这个提取到 xml 中来。

    1 <?xml version='1.0' encoding='utf-8'?>
    2 
    3 <Coder name="xiaoming" sex="man" love="girl" class="com.coder.Coder">
    4     <Girl class = "com.coder.Girl" name="Catalina" height="170" breast="C++" legLength="150" isPregnant="true" />
    5 </Coder>

     

    如上图所示,我们将其类型的信息提取到了 元素的class 属性中,以后假设想换个类型,那就很简单了,只要修改 class 属性即可。

    二、大体思路与具体实现

    1、Tomcat源码中的实现思路

    我们先截取了 Tomcat 中的 server.xml 一句:

    <Server port="8005" shutdown="SHUTDOWN">

     Tomcat 源码中负责定义如何解析上面这句的代码如下:

    1          //source:org.apache.catalina.startup.Catalina#createStartDigester
    2  
    3          digester.addObjectCreate("Server",
    4                                   "org.apache.catalina.core.StandardServer",
    5                                   "className");
    6          digester.addSetProperties("Server");
    7          digester.addSetNext("Server",
    8                              "setServer",
    9                              "org.apache.catalina.Server");

    我简单解释下,这里没有进行真实解析,只是定义解析规则,真实的解析发生在Digester.parse()方法中,彼时会回调这里定义的规则。 第三行表示,新增一条"Server"元素的规则,类型为ObjectCreate,这条规则,在遇到 "Server" 元素时,获取 className 属性的值,如果有的话,即创建指定类型的对象,否则默认创建 org.apache.catalina. core.StandardServer 类型的对象,并保存到 digester 对象的内部栈中; 第6行表示,新增一条 "Server" 元素的规则,类型为 SetAllPropertiesRule,这条规则,会从 digester 当前的栈中,取出栈顶对象,并利用反射,来将 xml 元素中的 attribute 设置到该对象中。

    2、仿写开始:自定义 rule 接口及实现

    package com.coder.rule;
    
    import org.xml.sax.Attributes;
    
    public interface ParseRule {
        /**
         * 遇到xml元素的开始标记时,调用该方法。
         * @param attributes 元素中的属性
         */
        void startElement(Attributes attributes);
    
        void body(String body);
    
        void endElement();
    }

     

    我们先定义了一个解析规则,规则中有三个方法,分别在遇到 xml元素 的开始标记、内容、结束标记时调用。接下来,我们再定义一个规则:

     1 package com.coder.rule;
     2 
     3 import com.coder.GirlFriendHandler;
     4 import com.coder.GirlFriendHandlerVersion2;
     5 import org.xml.sax.Attributes;
     6 
     7 /**
     8  * desc:
     9  *
    10  * @author : caokunliang
    11  * creat_date: 2019/7/1 0001
    12  * creat_time: 11:20
    13  **/
    14 public class CreateObjectParseRule implements ParseRule {
    15     private String attributeNameForObjectType;
    16 
    17     private ClassLoader loader;
    18 
    19     private GirlFriendHandlerVersion2 girlFriendHandler;
    20 
    21   
    23     public CreateObjectParseRule(String attributeNameForObjectType, GirlFriendHandlerVersion2 girlFriendHandler) {
    24         this.attributeNameForObjectType = attributeNameForObjectType;
    25         this.girlFriendHandler = girlFriendHandler;
    26         //默认使用当前线程类加载器
    27         loader = Thread.currentThread().getContextClassLoader();
    28     }
    29 
    30     @Override
    31     public void startElement(Attributes attributes) {
    32         String clazzStr = attributes.getValue(attributeNameForObjectType);
    33         if (clazzStr == null) {
    34             throw new RuntimeException("element must has attribute :" + attributeNameForObjectType);
    35         }
    36 
    37         Class<?> clazz;
    38         try {
    39             clazz = loader.loadClass(clazzStr);
    40         } catch (ClassNotFoundException e) {
    41             e.printStackTrace();
    42             throw new RuntimeException("class not found:" + clazzStr);
    43         }
    44 
    45         Object o;
    46         try {
    47             o = clazz.newInstance();
    48         } catch (InstantiationException | IllegalAccessException e) {
    49             e.printStackTrace();
    50             throw new RuntimeException("new instance failed.");
    51         }
    52 
    53         girlFriendHandler.push(o);
    54     }
    55 
    56     @Override
    57     public void body(String body) {
    58 
    59     }
    60 
    61     @Override
    62     public void endElement() {
    63 
    64     }
    65 }

     

    重点关注两个方法,一个是构造器,构造器两个参数,一个 attributeNameForObjectType 意思是要从xml元素的那个 属性中获取 对象类型,一个 girlFriendHandler 其实就是我们的解析器handler。

    然后要关注的方法是,startElement。32行,根据构造器中的attributeNameForObjectType 获取对应的对象类型,然后利用类加载器来加载该类,获取到class后,利用反射生成对象,并压入 handler的栈中。

    接下来,我们介绍另一个 rule:

      1 package com.coder.rule;
      2 
      3 import com.coder.GirlFriendHandlerVersion2;
      4 import com.coder.TwoTuple;
      5 import org.xml.sax.Attributes;
      6 
      7 import java.lang.reflect.InvocationTargetException;
      8 import java.lang.reflect.Method;
      9 import java.util.ArrayList;
     10 import java.util.Arrays;
     11 import java.util.List;
     12 import java.util.Objects;
     13 
     14 public class SetPropertiesParseRule implements ParseRule {
     15     private GirlFriendHandlerVersion2 girlFriendHandler;
     16 
     17     public SetPropertiesParseRule(GirlFriendHandlerVersion2 girlFriendHandler) {
     18         this.girlFriendHandler = girlFriendHandler;
     19     }
     20 
     21     @Override
     22     public void startElement(Attributes attributes) {
     23         Object object = girlFriendHandler.peek();
     24 
     25         setProperties(attributes,object);
     26     }
     27 
     28     @Override
     29     public void body(String body) {
     30 
     31     }
     32 
     33     @Override
     34     public void endElement() {
     35 
     36     }
     37 
     38     private void setProperties(Attributes attributes, Object object) {
     39         Method[] methods = object.getClass().getMethods();
     40         ArrayList<Method> list = new ArrayList<>();
     41         list.addAll(Arrays.asList(methods));
     42         list.removeIf(o -> o.getParameterCount() != 1);
     43 
     44 
     45         for (int i = 0; i < attributes.getLength(); i++) {
     46             // 获取属性名
     47             String attributesQName = attributes.getQName(i);
     48             String setterMethod = "set" + attributesQName.substring(0, 1).toUpperCase() + attributesQName.substring(1);
     49 
     50             String value = attributes.getValue(i);
     51             TwoTuple<Method, Object[]> tuple = getSuitableMethod(list, setterMethod, value);
     52             // 没有找到合适的方法
     53             if (tuple == null) {
     54                 continue;
     55             }
     56 
     57             Method method = tuple.first;
     58             Object[] params = tuple.second;
     59             try {
     60                 method.invoke(object,params);
     61             } catch (IllegalAccessException | InvocationTargetException e) {
     62                 e.printStackTrace();
     63             }
     64         }
     65     }
     66 
     67     private TwoTuple<Method, Object[]> getSuitableMethod(List<Method> list, String setterMethod, String value) {
     68 
     69         for (Method method : list) {
     70 
     71             if (!Objects.equals(method.getName(), setterMethod)) {
     72                 continue;
     73             }
     74 
     75             Object[] params = new Object[1];
     76 
     77             /**
     78              * 1;如果参数类型就是String,那么就是要找的
     79              */
     80             Class<?>[] parameterTypes = method.getParameterTypes();
     81             Class<?> parameterType = parameterTypes[0];
     82             if (parameterType.equals(String.class)) {
     83                 params[0] = value;
     84                 return new TwoTuple<>(method,params);
     85             }
     86 
     87             Boolean ok = true;
     88 
     89             // 看看int是否可以转换
     90             String name = parameterType.getName();
     91             if (name.equals("java.lang.Integer")
     92                     || name.equals("int")){
     93                 try {
     94                     params[0] = Integer.valueOf(value);
     95                 }catch (NumberFormatException e){
     96                     ok = false;
     97                     e.printStackTrace();
     98                 }
     99                 // 看看 long 是否可以转换
    100             }else if (name.equals("java.lang.Long")
    101                     || name.equals("long")){
    102                 try {
    103                     params[0] = Long.valueOf(value);
    104                 }catch (NumberFormatException e){
    105                     ok = false;
    106                     e.printStackTrace();
    107                 }
    108                 // 如果int 和 long 不行,那就只有尝试boolean了
    109             }else if (name.equals("java.lang.Boolean") ||
    110                     name.equals("boolean")){
    111                 params[0] = Boolean.valueOf(value);
    112             }
    113 
    114             if (ok){
    115                 return new TwoTuple<Method,Object[]>(method,params);
    116             }
    117         }
    118         return null;
    119     }
    120 }

     

    该 rule,重点代码为23-25行,主要是设置对象的属性。对象从哪来,从handler中获取栈顶元素即可。设置属性这部分,主要是利用反射来解决的。

    3、元素与规则列表的对应关系

    规则定义好了,我们再看看,针对具体某个xml元素,需要应用哪些规则呢? 这部分是需要我们预定义的。

     1 package com.coder;
     2 
     3 import com.coder.rule.CreateObjectParseRule;
     4 import com.coder.rule.ParseRule;
     5 import com.coder.rule.SetPropertiesParseRule;
     6 import org.xml.sax.Attributes;
     7 import org.xml.sax.SAXException;
     8 import org.xml.sax.helpers.DefaultHandler;
     9 
    10 import javax.xml.parsers.ParserConfigurationException;
    11 import javax.xml.parsers.SAXParser;
    12 import javax.xml.parsers.SAXParserFactory;
    13 import java.io.IOException;
    14 import java.io.InputStream;
    15 import java.lang.reflect.InvocationTargetException;
    16 import java.lang.reflect.Method;
    17 import java.util.*;
    18 import java.util.concurrent.ConcurrentHashMap;
    19 import java.util.concurrent.atomic.AtomicInteger;
    20 
    21 /**
    22  * desc:
    23  * @author: caokunliang
    24  * creat_date: 2019/6/29 0029
    25  * creat_time: 11:06
    26  **/
    27 public class GirlFriendHandlerVersion2 extends DefaultHandler {
    28     private LinkedList<Object> stack = new LinkedList<>();
    29 
    30     /**
    31      * 规则定义。每个元素可以有多条规则,所以value是一个list。解析时,会按顺序调用各个规则
    32      */
    33     private ConcurrentHashMap<String, List<ParseRule>> ruleMap = new ConcurrentHashMap<>();
    34 
    35     {
    36         ArrayList<ParseRule> rules = new ArrayList<>();
    37         rules.add(new CreateObjectParseRule("class",this));
    38         rules.add(new SetPropertiesParseRule(this));
    39 
    40         ruleMap.put("Coder",rules);
    41 
    42         rules = new ArrayList<>();
    43         rules.add(new CreateObjectParseRule("class",this));
    44         rules.add(new SetPropertiesParseRule(this));
    45 
    46         ruleMap.put("Girl",rules);
    47     }
    48 
    49 }

     

    为了存储该关系,我们利用了 concurrenthashmap,key即为xml元素的名字,value为需要应用的规则列表。具体的规则定义,见第 36-46行。

    4、startElement 实现

     @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            System.out.println("startElement: " + qName + " It's the " + eventOrderCounter.getAndIncrement() + " one");
    
            List<ParseRule> rules = ruleMap.get(qName);
            for (ParseRule rule : rules) {
                rule.startElement(attributes);
            }
    
        }

     

    该方法会在解析到 xml 元素开始时,被sax 解析模型调用。 第三个参数qName,即为xml元素的值。我们这里,根据qName获取到规则,然后依次应用这些规则。

    5、endElement实现

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            System.out.println("endElement: " + qName + " It's the " + eventOrderCounter.getAndIncrement() + " one");
    
            if ("Coder".equals(qName)){
                Object o = stack.pop();
                System.out.println(o);
            }else if ("Girl".equals(qName)){
                //弹出来的应该是girl
                Object o = stack.pop();
    
                //接下来获取到coder
                Coder coder = (Coder) stack.peek();
                coder.setGirl((Girl) o);
    
            }
        }

     

    该方法,会在解析到 xml元素的结束标记时被调用,我们这里,主要关注 橙色行,这里从栈中弹出第一个元素,应该是我们在 startElement 中 压入栈内的 girl;然后继续取栈顶元素,则应该取到 Coder 对象。然后我们这里,手动将 girl 设置到 Coder里面去。

    这里将在下一个版本的 handler 中进行优化。

    6、执行测试代码

    类的完整代码如下,执行main即可:

      1 package com.coder;
      2 
      3 import com.coder.rule.CreateObjectParseRule;
      4 import com.coder.rule.ParseRule;
      5 import com.coder.rule.SetPropertiesParseRule;
      6 import org.xml.sax.Attributes;
      7 import org.xml.sax.SAXException;
      8 import org.xml.sax.helpers.DefaultHandler;
      9 
     10 import javax.xml.parsers.ParserConfigurationException;
     11 import javax.xml.parsers.SAXParser;
     12 import javax.xml.parsers.SAXParserFactory;
     13 import java.io.IOException;
     14 import java.io.InputStream;
     15 import java.lang.reflect.InvocationTargetException;
     16 import java.lang.reflect.Method;
     17 import java.util.*;
     18 import java.util.concurrent.ConcurrentHashMap;
     19 import java.util.concurrent.atomic.AtomicInteger;
     20 
     21 /**
     22  * desc:
     23  * @author: caokunliang
     24  * creat_date: 2019/6/29 0029
     25  * creat_time: 11:06
     26  **/
     27 public class GirlFriendHandlerVersion2 extends DefaultHandler {
     28     private LinkedList<Object> stack = new LinkedList<>();
     29 
     30     /**
     31      * 规则定义。每个元素可以有多条规则,所以value是一个list。解析时,会按顺序调用各个规则
     32      */
     33     private ConcurrentHashMap<String, List<ParseRule>> ruleMap = new ConcurrentHashMap<>();
     34 
     35     {
     36         ArrayList<ParseRule> rules = new ArrayList<>();
     37         rules.add(new CreateObjectParseRule("class",this));
     38         rules.add(new SetPropertiesParseRule(this));
     39 
     40         ruleMap.put("Coder",rules);
     41 
     42         rules = new ArrayList<>();
     43         rules.add(new CreateObjectParseRule("class",this));
     44         rules.add(new SetPropertiesParseRule(this));
     45 
     46         ruleMap.put("Girl",rules);
     47     }
     48 
     49     private AtomicInteger eventOrderCounter = new AtomicInteger(0);
     50 
     51     @Override
     52     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
     53         System.out.println("startElement: " + qName + " It's the " + eventOrderCounter.getAndIncrement() + " one");
     54 
     55         List<ParseRule> rules = ruleMap.get(qName);
     56         for (ParseRule rule : rules) {
     57             rule.startElement(attributes);
     58         }
     59 
     60     }
     61 
     62 
     63 
     64     @Override
     65     public void endElement(String uri, String localName, String qName) throws SAXException {
     66         System.out.println("endElement: " + qName + " It's the " + eventOrderCounter.getAndIncrement() + " one");
     67 
     68         if ("Coder".equals(qName)){
     69             Object o = stack.pop();
     70             System.out.println(o);
     71         }else if ("Girl".equals(qName)){
     72             //弹出来的应该是girl
     73             Object o = stack.pop();
     74 
     75             //接下来获取到coder
     76             Coder coder = (Coder) stack.peek();
     77             coder.setGirl((Girl) o);
     78 
     79         }
     80     }
     81 
     82     public static void main(String[] args) {
     83         GirlFriendHandlerVersion2 handler = new GirlFriendHandlerVersion2();
     84 
     85         SAXParserFactory spf = SAXParserFactory.newInstance();
     86         try {
     87             SAXParser parser = spf.newSAXParser();
     88             InputStream inputStream = ClassLoader.getSystemClassLoader()
     89                     .getResourceAsStream("girlfriend.xml");
     90 
     91             parser.parse(inputStream, handler);
     92         } catch (ParserConfigurationException | SAXException | IOException e) {
     93             e.printStackTrace();
     94         }
     95     }
     96 
     97     /**
     98      * 栈内弹出栈顶对象
     99      * @return
    100      */
    101     public Object pop(){
    102         return stack.pop();
    103     }
    104 
    105     /**
    106      * 栈顶push元素
    107      * @param object
    108      */
    109     public void push(Object object){
    110         stack.push(object);
    111     }
    112 
    113     /**
    114      * 返回栈顶元素,但不弹出
    115      */
    116     public Object peek(){
    117         return stack.peek();
    118     }
    119 }
    View Code

     

    执行结果如下:

    三、优化

    这次的优化目标就是,去掉上面endElement里面的硬编码。我们给 girl 元素加一条rule,该rule 会在endElement时被调用,该rule的逻辑是,从栈中弹出 girl 元素,再从栈中取出栈顶元素(此时由于girl被弹出,此时栈顶为 coder)。

    然后直接反射调用 coder 的 setGirl 方法,即可将girl 设置进去。

    1、定义ParentChildRule 

     1 package com.coder.rule;
     2 
     3 import com.coder.GirlFriendHandlerVersion2;
     4 import org.xml.sax.Attributes;
     5 
     6 import java.lang.reflect.InvocationTargetException;
     7 import java.lang.reflect.Method;
     8 
     9 
    10 public class ParentChildRule implements ParseRule{
    11     /**
    12      * 父对象的方法名,通过该方法将子对象设置进去
    13      */
    14     private String parentObjectSetter;
    15 
    16     private GirlFriendHandlerVersion2 girlFriendHandler;
    17 
    18     public ParentChildRule(String parentObjectSetter, GirlFriendHandlerVersion2 girlFriendHandler) {
    19         this.parentObjectSetter = parentObjectSetter;
    20         this.girlFriendHandler = girlFriendHandler;
    21     }
    22 
    23     @Override
    24     public void startElement(Attributes attributes) {
    25 
    26     }
    27 
    28     @Override
    29     public void body(String body) {
    30 
    31     }
    32 
    33     @Override
    34     public void endElement() {
    35         // 获取到栈顶对象child,该对象将作为child,被设置到parent中
    36         Object child = girlFriendHandler.pop();
    37         //栈顶的child被弹出后,继续调用peek,将获取到parent
    38         Object parent = girlFriendHandler.peek();
    39 
    40         try {
    41             Method method = parent.getClass().getMethod(parentObjectSetter, new Class[]{child.getClass()});
    42             method.invoke(parent,child);
    43         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
    44             e.printStackTrace();
    45         }
    46     }
    47 }

    2、给 girl 新增规则

    1         rules = new ArrayList<>();
    2         rules.add(new CreateObjectParseRule("class",this));
    3         rules.add(new SetPropertiesParseRule(this));
    4         rules.add(new ParentChildRule("setGirl", this));
    5 
    6         ruleMap.put("Girl",rules);

    3、修改endElement

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            System.out.println("endElement: " + qName + " It's the " + eventOrderCounter.getAndIncrement() + " one");
    
            List<ParseRule> rules = ruleMap.get(qName);
            if (rules != null) {
                for (ParseRule rule : rules) {
                    rule.endElement();
                }
            }
    
        }

     

    这里的逻辑不再硬编码,根据元素获取 rule 列表,然后按顺序调用 rule 的 endElement 即可。这里,就会调用 ParentChildRule ,将 girl 设置到 coder里面去。

    4、完整实现

      1 package com.coder;
      2 
      3 import com.coder.rule.CreateObjectParseRule;
      4 import com.coder.rule.ParentChildRule;
      5 import com.coder.rule.ParseRule;
      6 import com.coder.rule.SetPropertiesParseRule;
      7 import org.xml.sax.Attributes;
      8 import org.xml.sax.SAXException;
      9 import org.xml.sax.helpers.DefaultHandler;
     10 
     11 import javax.xml.parsers.ParserConfigurationException;
     12 import javax.xml.parsers.SAXParser;
     13 import javax.xml.parsers.SAXParserFactory;
     14 import java.io.IOException;
     15 import java.io.InputStream;
     16 import java.util.*;
     17 import java.util.concurrent.ConcurrentHashMap;
     18 import java.util.concurrent.atomic.AtomicInteger;
     19 
     20 /**
     21  * desc:
     22  * @author: caokunliang
     23  * creat_date: 2019/6/29 0029
     24  * creat_time: 11:06
     25  **/
     26 public class GirlFriendHandlerVersion2 extends DefaultHandler {
     27     private LinkedList<Object> stack = new LinkedList<>();
     28 
     29     /**
     30      * 规则定义。每个元素可以有多条规则,所以value是一个list。解析时,会按顺序调用各个规则
     31      */
     32     private ConcurrentHashMap<String, List<ParseRule>> ruleMap = new ConcurrentHashMap<>();
     33 
     34     {
     35         ArrayList<ParseRule> rules = new ArrayList<>();
     36         rules.add(new CreateObjectParseRule("class",this));
     37         rules.add(new SetPropertiesParseRule(this));
     38 
     39         ruleMap.put("Coder",rules);
     40 
     41         rules = new ArrayList<>();
     42         rules.add(new CreateObjectParseRule("class",this));
     43         rules.add(new SetPropertiesParseRule(this));
     44         rules.add(new ParentChildRule("setGirl", this));
     45 
     46         ruleMap.put("Girl",rules);
     47     }
     48 
     49     private AtomicInteger eventOrderCounter = new AtomicInteger(0);
     50 
     51     @Override
     52     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
     53         System.out.println("startElement: " + qName + " It's the " + eventOrderCounter.getAndIncrement() + " one");
     54 
     55         List<ParseRule> rules = ruleMap.get(qName);
     56         for (ParseRule rule : rules) {
     57             rule.startElement(attributes);
     58         }
     59 
     60     }
     61 
     62 
     63 
     64     @Override
     65     public void endElement(String uri, String localName, String qName) throws SAXException {
     66         System.out.println("endElement: " + qName + " It's the " + eventOrderCounter.getAndIncrement() + " one");
     67 
     68         List<ParseRule> rules = ruleMap.get(qName);
     69         if (rules != null) {
     70             for (ParseRule rule : rules) {
     71                 rule.endElement();
     72             }
     73         }
     74 
     75     }
     76 
     77     public static void main(String[] args) {
     78         GirlFriendHandlerVersion2 handler = new GirlFriendHandlerVersion2();
     79 
     80         SAXParserFactory spf = SAXParserFactory.newInstance();
     81         try {
     82             SAXParser parser = spf.newSAXParser();
     83             InputStream inputStream = ClassLoader.getSystemClassLoader()
     84                     .getResourceAsStream("girlfriend.xml");
     85 
     86             parser.parse(inputStream, handler);
     87             Object o = handler.stack.pop();
     88             System.out.println(o);
     89         } catch (ParserConfigurationException | SAXException | IOException e) {
     90             e.printStackTrace();
     91         }
     92     }
     93 
     94     /**
     95      * 栈内弹出栈顶对象
     96      * @return
     97      */
     98     public Object pop(){
     99         return stack.pop();
    100     }
    101 
    102     /**
    103      * 栈顶push元素
    104      * @param object
    105      */
    106     public void push(Object object){
    107         stack.push(object);
    108     }
    109 
    110     /**
    111      * 返回栈顶元素,但不弹出
    112      */
    113     public Object peek(){
    114         return stack.peek();
    115     }
    116 }
    View Code

     

    四、源码与总结

    以上部分的源码在:

    https://github.com/cctvckl/tomcat-saxtest

     

    下篇将会正式进入 Tomcat 的 Digester 机制。

  • 相关阅读:
    好系统重装助手重装电脑系统步骤
    U盘加载速度慢的解决方法
    Win10应用商店缓存信息多如何去清理?
    怎么消除文件左上角的白色小框?
    U盘被识别但不显示盘符怎么样才能解决?
    【Gamma】Scrum Meeting 2
    【技术博客】 关于laravel5.1中文件上传测试的若干尝试
    【Beta】Phylab 发布说明
    【Beta】Phylab 测试报告
    【Beta】Scrum Meeting 10
  • 原文地址:https://www.cnblogs.com/grey-wolf/p/11114336.html
Copyright © 2020-2023  润新知