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)
原文地址:https://www.cnblogs.com/muzen/p/8676252.html