• Tomcat源码学习(3)—— Digester介绍


    Digester方法详解:

    1. 通读Digester之前先分析下他的结构:
      1.1该类继承了方法DefaultHandler2,DefaultHandler2继承了DefaultHandler是和sax解析器配合使用的类。当sax在对字符流进行加工的时候会根据实际情况调用 DefaultHandler中的方法。其使用的设计模式为模板模式。

            1.2createStartDigester方法中addObjectCreate、addSetNext的参数最终都是继承自抽象类Rule;方法addRuleSet的参数继承自抽象类RuleSetBase,实现接口RuleSet
      
            1.3总结:理解digester前需要了解的接口和抽象类:DefaultHandler、Rule、RuleSet。
      
    2. DefaultHandler类:
      DefaultHandler类中的方法:processingInstruction、ignorableWhitespace、characters、endElement、startElement、endPrefixMapping、startPrefixMapping、endDocument、startDocument、setDocumentLocator、resolveEntity等方法。上面所列出的大多为digester中需要用到的

    3. Rule类:主要四个方法 begin 、body、end、finish

    4. RuleSet类:addRuleInstances

    5. 关系梳理:
      (1)catalina类的load方法中两个主要的createStartDigester和digester.parse方法。我们先理解parse方法;createStartDigester方法到后面再说。

    (2) digester.parse()是用sax来解析xml的。

             2.1根据其使用的模板设计模式,会在  解析xml节点时调用DefaultHandler类中方法。当调用startDocument时会   调用rule.begin。代码如下:该处的rule使用的是接口,实际调用中,是使用的那个实现类,要看rules.get()拿出的对象是什么。这个在后面学习createStartDigester会再提到。
    

    [java] view plain copy
    public void startElement(String namespaceURI, String localName, String qName, Attributes list) throws SAXException {
    boolean debug = log.isDebugEnabled();

        if (saxLog.isDebugEnabled()) {  
            saxLog.debug("startElement(" + namespaceURI + "," + localName + "," + qName + ")");  
        }  
    
        // Parse system properties  
        list = updateAttributes(list);  
    
        // Save the body text accumulated for our surrounding element  
        bodyTexts.push(bodyText);  
        if (debug) {  
            log.debug("  Pushing body text '" + bodyText.toString() + "'");  
        }  
        bodyText = new StringBuffer();  
    
        // the actual element name is either in localName or qName, depending  
        // on whether the parser is namespace aware  
        String name = localName;  
        if ((name == null) || (name.length() < 1)) {  
            name = qName;  
        }  
    
        // Compute the current matching rule  
        StringBuffer sb = new StringBuffer(match);  
        if (match.length() > 0) {  
            sb.append('/');  
        }  
        sb.append(name);  
        match = sb.toString();  
        if (debug) {  
            log.debug("  New match='" + match + "'");  
        }  
    
        // Fire "begin" events for all relevant rules  
        List rules = getRules().match(namespaceURI, match);  
        matches.push(rules);  
        if ((rules != null) && (rules.size() > 0)) {  
            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 + "'.");  
            }  
        }  
    
    }  
         2.2其余的方法endDocument调用rule.finish、endElement调用rule.body方法等就不在一一列举,代码中很清楚。
    

    [java] view plain copy
    public void endDocument() throws SAXException {

    if (saxLog.isDebugEnabled()) {  
        if (getCount() > 1) {  
            saxLog.debug("endDocument():  " + getCount() + " elements left");  
        } else {  
            saxLog.debug("endDocument()");  
        }  
    }  
    
    while (getCount() > 1) {  
        pop();  
    }  
    
    // Fire "finish" events for all defined rules  
    Iterator rules = getRules().rules().iterator();  
    while (rules.hasNext()) {  
        Rule rule = (Rule) rules.next();  
        try {  
            rule.finish();  
        } catch (Exception e) {  
            log.error("Finish event threw exception", e);  
            throw createSAXException(e);  
        } catch (Error e) {  
            log.error("Finish event threw error", e);  
            throw e;  
        }  
    }  
    
    // Perform final cleanup  
    clear();  
    

    }

    (3)createStartDigester方法中会创建一些对象的实例,并调用其中的一些方法。用的方式大多为java中的反射机制。

      3.1其中addObjectCreate会创建ObjectCreateRule对象,该对象继承自Rule方法,它实现了rule的begin方法,在其中创建了类的实例。
    

    [java] view plain copy
    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);  
    

    }
    3.2 addSetProperties主要是解析xml节点中的属性值,实现了rule接口的begin方法。

    [java] view plain copy
    public void begin(Attributes attributes) throws Exception {

    // Populate the corresponding properties of the top object  
    Object top = digester.peek();  
    if (digester.log.isDebugEnabled()) {  
        if (top != null) {  
            digester.log.debug("[SetPropertiesRule]{" + digester.match +  
                               "} Set " + top.getClass().getName() +  
                               " properties");  
        } else {  
            digester.log.debug("[SetPropertiesRule]{" + digester.match +  
                               "} Set NULL properties");  
        }  
    }  
      
    // set up variables for custom names mappings  
    int attNamesLength = 0;  
    if (attributeNames != null) {  
        attNamesLength = attributeNames.length;  
    }  
    int propNamesLength = 0;  
    if (propertyNames != null) {  
        propNamesLength = propertyNames.length;  
    }  
      
    for (int i = 0; i < attributes.getLength(); i++) {  
        String name = attributes.getLocalName(i);  
        if ("".equals(name)) {  
            name = attributes.getQName(i);  
        }  
        String value = attributes.getValue(i);  
          
        // we'll now check for custom mappings  
        for (int n = 0; n<attNamesLength; n++) {  
            if (name.equals(attributeNames[n])) {  
                if (n < propNamesLength) {  
                    // set this to value from list  
                    name = propertyNames[n];  
                  
                } else {  
                    // set name to null  
                    // we'll check for this later  
                    name = null;  
                }  
                break;  
            }  
        }   
          
        if (digester.log.isDebugEnabled()) {  
            digester.log.debug("[SetPropertiesRule]{" + digester.match +  
                    "} Setting property '" + name + "' to '" +  
                    value + "'");  
        }  
        if (!digester.isFakeAttribute(top, name)   
                && !IntrospectionUtils.setProperty(top, name, value)   
                && digester.getRulesValidation()) {  
            digester.log.warn("[SetPropertiesRule]{" + digester.match +  
                    "} Setting property '" + name + "' to '" +  
                    value + "' did not find a matching property.");  
        }  
    }  
    

    }
    3.3 addSetNext方法会创建一个SetNextRule对象,该对象实现了rule的end方法,并在其中调用了指定类中的指定方法。
    [java] view plain copy
    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  
    IntrospectionUtils.callMethod1(parent, methodName,  
            child, paramType, digester.getClassLoader());  
    

    }
    3.4 addRule方法就是在规则列表中,简单的添加一条规则。不同的是,实现的方法是rules接口中的add方法。添加的规则有:ConnectorCreateRule、SetAllPropertiesRule、SetParentClassLoaderRule。其中ConnectorCreateRule就是创建一个connector。
    [java] view plain copy
    public void addRule(String pattern, Rule rule) {

    rule.setDigester(this);  
    getRules().add(pattern, rule);  
    

    }
    RulesBase中的方法:
    [java] view plain copy
    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);  
    }  
    

    }

                  3.5addRuleSet方法添加的都是继承自RuleSet的对象,重写了方法addRuleInstances的逻辑。从代码中可以清晰的看到,添加的是tomcat中的engin、host等信息。
    

    [java] view plain copy
    // Add RuleSets for nested elements
    digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
    digester.addRuleSet(new EngineRuleSet("Server/Service/"));
    digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
    digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
    digester.addRuleSet(ClusterRuleSetFactory.getClusterRuleSet("Server/Service/Engine/Host/Cluster/"));
    digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));

    // When the 'engine' is found, set the parentClassLoader.
    digester.addRule("Server/Service/Engine",
    new SetParentClassLoaderRule(parentClassLoader));
    digester.addRuleSet(ClusterRuleSetFactory.getClusterRuleSet("Server/Service/Engine/Cluster/"));
    在对象的实现中,addObjectCreate目的是创建对象,setNextSet是调用该对象的指定方法。比如NamingRuleSet对象实现Ejb、Environment、LocalEjb、Resource、Transaction等的初始化动作。EngineRuleSet对象实现Engine和其下的Cluster、Listener、Valve的初始化动作。HostRuleSet对象实现Host和其下的Alias、Cluster、Listener、Valve的初始化。ContextRuleSet对象主要实现Context、Listener、WebappLoader、Manager、Store、Parameter、Resources、Valve等的初始化。

    1. 总结:
      读懂digester,关键是需要理解DefaultHandler、Rule、RuleSet这三个抽象类和接口的关联关系,了解其使用的设计模式,将其串联起来就容易了。我在读代码时,最难理解的是,不知道为何一句parse就能把server.xml中的配置给加载完了,最后通过学习别人的文章后,逐渐弄懂了。以此记录,继续学习。

    关于Digester何时调用startElement方法,我目前还没搞懂。但是从调用堆栈可以推测一下。

        "main@1" prio=5 tid=0x1 nid=NA runnable  java.lang.Thread.State: RUNNABLE
      at org.apache.tomcat.util.digester.Digester.startElement(Digester.java:1120)
      at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:509)
      at com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidator.startElement(XMLDTDValidator.java:746)
      at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanStartElement(XMLDocumentFragmentScannerImpl.java:1394)
      at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$ContentDriver.scanRootElementHook(XMLDocumentScannerImpl.java:1251)
      at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:3058)
      at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:820)
      at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:601)
      at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:531)
      at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:887)
      at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:823)
      at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
      at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1213)
      at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:639)
      at org.apache.tomcat.util.digester.Digester.parse(Digester.java:1419)
      at org.apache.catalina.startup.Catalina.load(Catalina.java:601)
      at org.apache.catalina.startup.Catalina.load(Catalina.java:652)
      at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1)
      at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:564)
      at org.apache.catalina.startup.Bootstrap.load(Bootstrap.java:309)
      at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:492)
  • 相关阅读:
    制作自己的Docker镜像
    Docker 常见应用部署
    一文读懂Docker相关命令
    linux在下软件太卡?手把手教你配置国内镜像源
    2013年蓝桥杯省赛C组笔记
    java基本数据类型之间的转换
    h5中的分组元素figure、figcaption、hgroup元素介绍
    初识WSGI接口
    h5中的结构元素header、nav、article、aside、section、footer介绍
    提交 linux kernel 补丁流程备忘录
  • 原文地址:https://www.cnblogs.com/muzen/p/8676252.html
Copyright © 2020-2023  润新知