tomcat源码阅读(二) Digester方法详解

来源:互联网 发布:mac刷新桌面 编辑:程序博客网 时间:2024/05/16 07:48

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会再提到。

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方法等就不在一一列举,代码中很清楚。

    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方法,在其中创建了类的实例。

    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方法。

    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对象,该对象实现了ruleend方法,并在其中调用了指定类中的指定方法。
    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。

    public void addRule(String pattern, Rule rule) {        rule.setDigester(this);        getRules().add(pattern, rule);    }
        RulesBase中的方法:
    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等信息。

        // 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等的初始化。

6.  总结:

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

0 0
原创粉丝点击