tomcat Digester分析

tomcat中使用Digester进行xml to javaobject的转换,下面分析下其实现原理

Digester类继承自DefaultHandler,当xmlreader进行处理XML的时候,将事件通知指向Digester类来处理

那么当reader进行startElement 和endElemnet的时候对Digester进行通知进行相应的处理

例如:

<server>

    <listener />

    <service />

</server>

那么处理逻辑可以这来解释当遇到begin server的时候 创建server对象,当遇到listener时候创建Listener对象,当遇到Listener end的时候进行server.addlistener操作

但是这样的逻辑太当换了类了后,程序是不会自己去识别对应的类(也是不可能识别的) 所以如果想要支持你所想支持的类那么必须要自己进行配置,这里Digester框架使用了策略模式。

那么先看一段源码

digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");
        digester.addSetProperties("Server");
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");

        digester.addObjectCreate("Server/GlobalNamingResources",
                                 "org.apache.catalina.deploy.NamingResources");
        digester.addSetProperties("Server/GlobalNamingResources");
        digester.addSetNext("Server/GlobalNamingResources",
                            "setGlobalNamingResources",
                            "org.apache.catalina.deploy.NamingResources");

 可以看出在使用Digester 你必须要自己告诉程序当遇到不同的元素要进行怎样的处理

 上述为当遇到Server元素的时候,进行org.apache.catalina.core.StandardServer的穿件,并赋予属性给类的属性,并将StandardServer添加到根元素。

那么其中的内部实现其实是这样的

在Digester类中实现了startElement方法以及end方法等,当reader通知Digester 后调用startElment后逐个调用每个元素自己的规则来进行处理,所以就有了上面的一段代码进行规则制定,那么这些规则又是怎么实现的

下面是Rule的基类

public abstract class Rule {

    /**
     * This method is called when the beginning of a matching XML element
     * is encountered. The default implementation delegates to the deprecated
     * method {@link #begin(Attributes) begin} without the 
     * <code>namespace</code> and <code>name</code> parameters, to retain 
     * backwards compatibility.
     *
     * @param namespace the namespace URI of the matching element, or an 
     *   empty string if the parser is not namespace aware or the element has
     *   no namespace
     * @param name the local name if the parser is namespace aware, or just 
     *   the element name otherwise
     * @param attributes The attribute list of this element
     * @since Digester 1.4
     */
    public void begin(String namespace, String name, Attributes attributes)
        throws Exception {

        begin(attributes);

    }

    /**
     * This method is called when the body of a matching XML element
     * is encountered.  If the element has no body, this method is
     * not called at all.
     *
     * @param text The text of the body of this element
     * @deprecated Use the {@link #body(String,String,String) body} method
     *   with <code>namespace</code> and <code>name</code> parameters
     *   instead.
     */
    @Deprecated
    public void body(String text) throws Exception {
        // The default implementation does nothing
    }

    /**
     * This method is called when the body of a matching XML element is 
     * encountered.  If the element has no body, this method is not called at 
     * all. The default implementation delegates to the deprecated method 
     * {@link #body(String) body} without the <code>namespace</code> and
     * <code>name</code> parameters, to retain backwards compatibility.
     *
     * @param namespace the namespace URI of the matching element, or an 
     *   empty string if the parser is not namespace aware or the element has
     *   no namespace
     * @param name the local name if the parser is namespace aware, or just 
     *   the element name otherwise
     * @param text The text of the body of this element
     * @since Digester 1.4
     */
    public void body(String namespace, String name, String text)
        throws Exception {

        body(text);

    }

    /**
     * This method is called when the end of a matching XML element
     * is encountered.
     * 
     * @deprecated Use the {@link #end(String,String) end} method with 
     *   <code>namespace</code> and <code>name</code> parameters instead.
     */
    @Deprecated
    public void end() throws Exception {
        // The default implementation does nothing
    }


    /**
     * This method is called when the end of a matching XML element
     * is encountered. The default implementation delegates to the deprecated
     * method {@link #end end} without the 
     * <code>namespace</code> and <code>name</code> parameters, to retain 
     * backwards compatibility.
     *
     * @param namespace the namespace URI of the matching element, or an 
     *   empty string if the parser is not namespace aware or the element has
     *   no namespace
     * @param name the local name if the parser is namespace aware, or just 
     *   the element name otherwise
     * @since Digester 1.4
     */
    public void end(String namespace, String name)
        throws Exception {

        end();

    }

    /**
     * This method is called after all parsing methods have been
     * called, to allow Rules to remove temporary data.
     */
    public void finish() throws Exception {
        // The default implementation does nothing
    }


}

 主要四个方法 begin 、body、end、finish

 而对于元素的处理便会先进行rule的选取,然后对每个rule进行上面四个方法的处理,例如:addObjectCreate的方法实现为

    public void addObjectCreate(String pattern, String className,
                                String attributeName) {

        addRule(pattern,
                new ObjectCreateRule(className, attributeName));

    }

 当对SERVER元素进行addObjectCreate的时候实际上等于是给SERVER添加了一个RULE,那么整个处理过程为怎么回事呢,首先贴出Digester类中中startElement方法(只贴出主要部分)

 if ((rules != null) && (rules.size() > 0)) {
            for (int i = 0; i < rules.size(); i++) {
                try {
                    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,进行begin方法的调用

而endElement方法思路也基本是这样只不过是调用了RULE的body和end方法,而finish()方法是当XML文档reader完成后进行调用。并且当在startElement方法中调用RULE的时候是按照添加RULE的先后顺序进行调用,EndElement方法则是想法的顺序进行调用,谁让xml就是这么个规则。

  最后挑选其中的一个RULE的子类(具体实现类)的代码分析,当然有N个这样的子类,下面贴出ObjectCreateRule的部分代码,也就是当调用Digester.addObjectCreate方法后添加的RULE

首先贴出begin代码

public void begin(String namespace, String name, 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);
        }

        if (realClassName == null) {
            throw new NullPointerException("No class name specified for " +
                    namespace + " " + name);
        }

        // Instantiate the new object and push it on the context stack
        Class<?> clazz = digester.getClassLoader().loadClass(realClassName);
        Object instance = clazz.newInstance();
        digester.push(instance);
    }

 可以看出其中的逻辑为如果有classname这个属性的话,那么这个对象instance的时候 要进行子类的instance,并且把instance的对象方法stack中,digester.push方法实际上就是放入Digester类的stack字段中,而整个Digester对类的操作都是在stack中进行的,当然这个原因也是xml的规则所决定的。

那么贴出ObjectCreateRule的end代码

public void end(String namespace, String name) throws Exception {

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

    }

 可以看出instance进行了出栈操作

 当然还有其他很多的RULE,比如属性赋值,添加child等等。当然既然知道了RULE的实现也可以自我定制自我的RULE。

猜你喜欢

转载自wanglonglog.iteye.com/blog/1628471
今日推荐