Digester解析xml原理

Tomcat内部是使用Digester来解析xml文件的,将xml转化为java对象。

digester底层是基于SAX+事件驱动+栈的方式来搭建实现的,SAX主要用来解析xml,事件驱动主要是在解析的过程中加入事件来操作节点元素,栈主要是在节点解析开始和结束时对xml节点元素对应的对象操作入栈或出栈来实现事件的调用。

使用方法

定义一个Department部门类以及一个User用户类,部门中包含许多个用户

@Data
public class Department {

  private String departmentName;

  private List<user> userList;

  public Department() {
    userList = new ArrayList<>();
  }

  public void addUser(User user) {
    userList.add(user);
  }

}</user>
@Data
public class User {

  private String userName;

  private String age;

  public void print() {
    System.out.println("userName:" + userName + ", age:" + age);
  }
}

xml文件

<?xml version="1.0" encoding="UTF-8"?>

<department departmentname="&#x4EA7;&#x54C1;&#x6280;&#x672F;&#x90E8;">

  <user username="monian" age="25">&#x9ED8;&#x5FF5;</user>
</department>

定义规则解析xml文件

  1. ObjectCreateRule规则类

public void addObjectCreate(String pattern, String className, String attributeName) 当匹配到pattern模式节点时会创建对象,当在节点中指定了attributeName属性时,会创建类型为attributeName属性值的对象否则创建类名为className的对象,并将创建的对象push到stack栈顶

  1. SetPropertiesRule规则类

public void addSetProperties(String pattern)当匹配到pattern模式节点时会填充stack栈顶元素对象的属性值

  1. CallMethodRule规则类

public void addCallMethod(String pattern, String methodName) (无参)当匹配到pattern模式节点时会调用stack栈顶元素对象的methodName方法

public void addCallMethod(String pattern, String methodName, int paramCount)(有参,指定参数个数与CallParamRule规则配合使用,设置参数值),构建空的参数数组并push到params栈顶

  1. CallParamRule规则类

public void addCallParam(String pattern, int paramIndex)&#xA0; 当匹配到pattern模式时,以pattern模式节点的内容填充params栈顶元素参数的值

  1. SetNextRule规则类

public void addSetNext(String pattern, String methodName, String paramType) 当匹配到pattern模式时,调用栈顶元素的上一个元素的methodName方法并以栈顶元素作为参数

  1. 自定义规则类

public void addRule(String pattern, Rule rule)当匹配到pattern模式时,执行自定义的规则

public class DigesterTest {

  public static void main(String[] args) throws IOException, SAXException {
    InputStream resource
        = ClassLoader.getSystemClassLoader().getResourceAsStream("test.xml");

    Digester digester = new Digester();
    digester.setValidating(false);
    digester.setRulesValidation(true);

    // &#x521B;&#x5EFA;&#x5BF9;&#x8C61;&#x89C4;&#x5219;
    digester.addObjectCreate("department", Department.class.getName());
    // &#x586B;&#x5145;&#x5C5E;&#x6027;&#x89C4;&#x5219;
    digester.addSetProperties("department");

    digester.addObjectCreate("department/user", User.class.getName());
    digester.addSetProperties("department/user");
    // &#x8C03;&#x7528;&#x65B9;&#x6CD5;&#x89C4;&#x5219;
    digester.addCallMethod("department/user", "print");
    // &#x8C03;&#x7528;&#x6808;&#x9876;&#x5143;&#x7D20;&#x4E0A;&#x4E00;&#x4E2A;&#x5143;&#x7D20;&#x7684;&#x6307;&#x5B9A;&#x65B9;&#x6CD5;&#xFF0C;&#x4EE5;&#x6808;&#x9876;&#x5143;&#x7D20;&#x4F5C;&#x4E3A;&#x53C2;&#x6570;
    digester.addSetNext("department/user", "addUser", User.class.getName());

    // &#x89E3;&#x6790;test.xml&#x6587;&#x4EF6; &#x83B7;&#x53D6;department&#x5BF9;&#x8C61;
    Department department = (Department) digester.parse(resource);
    System.out.println(department);
  }

}

运行程序可以看到调用digester的parse方法后成功解析获得department对象

Digester解析xml原理

原理

上面列出的规则类都继承了Rule这个抽象类,能够在匹配pattern模式时执行相应的事件方法,让我们看看Rule中定义了哪些方法

/**
 * Concrete implementations of this class implement actions to be taken when
 * a corresponding nested pattern of XML elements has been matched.

 */
public abstract class Rule {

    // ----------------------------------------------------------- Constructors

    /**
     * <p>Base constructor.

     * Now the digester will be set when the rule is added.</p>
     */
    public Rule() {}

    // ----------------------------------------------------- Instance Variables

    /**
     * The Digester with which this Rule is associated.

     */
    // &#x8FD9;&#x4E2A;&#x89C4;&#x5219;&#x5173;&#x8054;&#x7684;digester
    protected Digester digester = null;

    /**
     * The namespace URI for which this Rule is relevant, if any.

     */
    protected String namespaceURI = null;

    // ------------------------------------------------------------- Properties

    /**
     * Identify the Digester with which this Rule is associated.

     *
     * @return the Digester with which this Rule is associated.

     */
    public Digester getDigester() {
        return digester;
    }

    /**
     * Set the <code>Digester</code> with which this <code>Rule</code> is
     * associated.

     *
     * @param digester The digester with which to associate this rule
     */
    public void setDigester(Digester digester) {
        this.digester = digester;
    }

    /**
     * Return the namespace URI for which this Rule is relevant, if any.

     *
     * @return The namespace URI for which this rule is relevant or
     *         <code>null</code> if none.

     */
    public String getNamespaceURI() {
        return namespaceURI;
    }

    /**
     * Set the namespace URI for which this Rule is relevant, if any.

     *
     * @param namespaceURI Namespace URI for which this Rule is relevant,
     *  or <code>null</code> to match independent of namespace.

     */
    public void setNamespaceURI(String namespaceURI) {
        this.namespaceURI = namespaceURI;
    }

    // --------------------------------------------------------- Public Methods

    /**
     * This method is called when the beginning of a matching XML element
     * is encountered. The default implementation is a NO-OP.

     *
     * @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 &#x8282;&#x70B9;&#x5B9A;&#x4E49;&#x7684;&#x547D;&#x540D;&#x7A7A;&#x95F4;
     * @param name the local name if the parser is namespace aware, or just
     *             the element name otherwise  &#x8282;&#x70B9;&#x540D;&#x79F0;
     * @param attributes The attribute list of this element  &#x8282;&#x70B9;&#x5C5E;&#x6027;&#x503C;&#x5217;&#x8868;
     *
     * @throws Exception if an error occurs while processing the event
     */
    // &#x9047;&#x5230;&#x5339;&#x914D;xml&#x5143;&#x7D20;&#x7684;&#x5F00;&#x5934;&#x662F;&#x8C03;&#x7528;&#x6B64;&#x65B9;&#x6CD5;
    public void begin(String namespace, String name, Attributes attributes) throws Exception {
        // NO-OP by default.

    }

    /**
     * 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 is a NO-OP.

     *
     * @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 &#x8282;&#x70B9;&#x5B9A;&#x4E49;&#x7684;&#x547D;&#x540D;&#x7A7A;&#x95F4;
     * @param name the local name if the parser is namespace aware, or just the
     *             element name otherwise &#x8282;&#x70B9;&#x540D;&#x79F0;
     * @param text The text of the body of this element &#x8282;&#x70B9;&#x6587;&#x672C;&#x5185;&#x5BB9;
     *
     * @throws Exception if an error occurs while processing the event
     */
    //  &#x9047;&#x5230;&#x5339;&#x914D;&#x7684; XML &#x5143;&#x7D20;&#x7684;&#x4E3B;&#x4F53;&#x65F6;&#x8C03;&#x7528;&#x6B64;&#x65B9;&#x6CD5;&#xFF0C; &#x5185;&#x5BB9;&#x4E3A;&#x7A7A;&#x7684;&#x8BDD;&#x4E0D;&#x8C03;&#x7528;
    public void body(String namespace, String name, String text) throws Exception {
        // NO-OP by default.

    }

    /**
     * This method is called when the end of a matching XML element
     * is encountered. The default implementation is a NO-OP.

     *
     * @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
     *
     * @throws Exception if an error occurs while processing the event
     */
    // &#x5F53;&#x9047;&#x5230;&#x5339;&#x914D;&#x7684; XML &#x5143;&#x7D20;&#x7684;&#x7ED3;&#x5C3E;&#x65F6;&#x8C03;&#x7528;&#x6B64;&#x65B9;&#x6CD5;&#x3002;
    public void end(String namespace, String name) throws Exception {
        // NO-OP by default.

    }

    /**
     * This method is called after all parsing methods have been
     * called, to allow Rules to remove temporary data.

     *
     * @throws Exception if an error occurs while processing the event
     */
    // &#x6240;&#x6709;&#x89E3;&#x6790;&#x65B9;&#x6CD5;&#x8C03;&#x7528;&#x540E;&#x8C03;&#x7528;&#x6B64;&#x65B9;&#x6CD5;&#xFF0C;&#x5141;&#x8BB8;&#x89C4;&#x5219;&#x5220;&#x9664;&#x4E34;&#x65F6;&#x4EA7;&#x751F;&#x7684;&#x6570;&#x636E;
    public void finish() throws Exception {
        // NO-OP by default.

    }
}

接下来再来看看Digester的几个重要属性和方法

// &#x7EE7;&#x627F;&#x4E86;SAX&#x7684;DefaultHandler&#x7C7B;&#xFF0C;&#x4F1A;&#x5728;&#x89E3;&#x6790;&#x8FC7;&#x7A0B;&#x4E2D;&#x63A5;&#x53D7;&#x5230;&#x76F8;&#x5E94;&#x7684;&#x901A;&#x77E5;
public class Digester extends DefaultHandler2 {

    // &#x7528;&#x6765;&#x89E3;&#x6790;&#x5360;&#x4F4D;&#x7B26;&#x5C5E;&#x6027; ${xxx}, &#x4E3B;&#x8981;&#x4ECE;System.getProperty(xxx)&#x83B7;&#x53D6;
    protected IntrospectionUtils.PropertySource source[] = new IntrospectionUtils.PropertySource[] {
            new SystemPropertySource() };

    // &#x5F53;&#x524D;&#x6B63;&#x5728;&#x89E3;&#x6790;&#x7684;&#x8282;&#x70B9;&#x5185;&#x5BB9;
    protected StringBuilder bodyText = new StringBuilder();

    // &#x89E3;&#x6790;&#x8FC7;&#x7A0B;&#x4E2D;&#x4EA7;&#x751F;&#x7684;&#x8282;&#x70B9;&#x5185;&#x5BB9;&#x5806;&#x6808;
    protected ArrayStack<stringbuilder> bodyTexts = new ArrayStack<>();

    // &#x89E3;&#x6790;&#x8FC7;&#x7A0B;&#x4E2D;&#x5B58;&#x50A8;&#x89C4;&#x5219;&#x5217;&#x8868;&#x7684;&#x5806;&#x6808; list&#x4E2D;&#x7684;&#x6BCF;&#x4E2A;&#x89C4;&#x5219;&#x6709;&#x76F8;&#x540C;&#x7684;pattern
    protected ArrayStack<list<rule>> matches = new ArrayStack<>(10);

    // &#x5D4C;&#x5957;&#x5143;&#x7D20;&#x5904;&#x7406;&#x7684;&#x5F53;&#x524D;&#x5339;&#x914D;&#x6A21;&#x5F0F; &#x4F8B;&#x5982; department &#x3001; department/user
    protected String match = "";

    // &#x5B58;&#x50A8;&#x65B9;&#x6CD5;&#x53C2;&#x6570;&#x7684;&#x5806;&#x6808;
    protected ArrayStack<object> params = new ArrayStack<>();

    // &#x6839;&#x8282;&#x70B9;&#x5143;&#x7D20;&#xFF0C;&#x6700;&#x540E;&#x51FA;&#x6808;stack&#x7684;&#x90A3;&#x4E2A;&#x5143;&#x7D20;
    protected Object root = null;

    // &#x5B9E;&#x73B0;&#x7C7B;&#x4E3A;RulesBase&#xFF0C;&#x62E5;&#x6709;cache&#x5C5E;&#x6027;&#x5B58;&#x50A8;&#x4E86;pattern&#x548C;&#x89C4;&#x5219;&#x5217;&#x8868;&#x7684;&#x6620;&#x5C04;&#xFF0C;&#x80FD;&#x6839;&#x636E;pattern&#x83B7;&#x53D6;&#x89C4;&#x5219;&#x5217;&#x8868;
    protected Rules rules = null;

    // &#x5B58;&#x50A8;&#x65B0;&#x521B;&#x5EFA;&#x5BF9;&#x8C61;&#x7684;&#x5806;&#x6808;
    protected ArrayStack<object> stack = new ArrayStack<>();

    // &#x5047;&#x5C5E;&#x6027;&#x6620;&#x5C04;&#xFF08;&#x901A;&#x5E38;&#x7528;&#x4E8E;&#x5BF9;&#x8C61;&#x521B;&#x5EFA;&#xFF09;
    protected Map<class<?>, List<string>> fakeAttributes = null;

    // &#x63A5;&#x6536;&#x5143;&#x7D20;&#x5185;&#x5B57;&#x7B26;&#x6570;&#x636E;&#x7684;&#x901A;&#x77E5;
    @Override
    public void characters(char buffer[], int start, int length) throws SAXException {

        if (saxLog.isDebugEnabled()) {
            saxLog.debug("characters(" + new String(buffer, start, length) + ")");
        }

        bodyText.append(buffer, start, length);
    }

    //&#x5904;&#x7406;&#x5230;&#x8FBE; XML &#x5143;&#x7D20;&#x5F00;&#x59CB;&#x7684;&#x901A;&#x77E5;
        @Override
    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
        // &#x89E3;&#x6790;&#x7CFB;&#x7EDF;&#x5C5E;&#x6027;&#xFF08;&#x82E5;&#x6709;&#xFF09;
        list = updateAttributes(list);

        // Save the body text accumulated for our surrounding element
        bodyTexts.push(bodyText);
        bodyText = new StringBuilder();

        // 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
        // &#x8BA1;&#x7B97;&#x5F53;&#x524D;&#x7684;pattern&#x89C4;&#x5219;
        StringBuilder sb = new StringBuilder(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
        // &#x83B7;&#x53D6;&#x4E0E;pattern&#x6A21;&#x5F0F;&#x5339;&#x914D;&#x7684;&#x89C4;&#x5219;
        List<rule> rules = getRules().match(namespaceURI, match);
        // push&#x8FDB;matches&#x6808;
        matches.push(rules);
        // &#x904D;&#x5386;&#x89C4;&#x5219;&#x6267;&#x884C;&#x6BCF;&#x4E2A;&#x89C4;&#x5219;&#x7684;begin&#x65B9;&#x6CD5;
        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 + "'.");
            }
        }

    }

    // &#x63A5;&#x6536;&#x5143;&#x7D20;&#x7ED3;&#x675F;&#x7684;&#x901A;&#x77E5;
        @Override
    public void endElement(String namespaceURI, String localName, String qName)
            throws SAXException {

        boolean debug = log.isDebugEnabled();

        if (debug) {
            if (saxLog.isDebugEnabled()) {
                saxLog.debug("endElement(" + namespaceURI + "," + localName + "," + qName + ")");
            }
            log.debug("  match='" + match + "'");
            log.debug("  bodyText='" + bodyText + "'");
        }

        // Parse system properties
        // &#x83B7;&#x53D6;&#x8282;&#x70B9;&#x5143;&#x7D20;&#x5185;&#x5BB9;
        bodyText = updateBodyText(bodyText);

        // 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;
        }

        // Fire "body" events for all relevant rules
        // &#x5F39;&#x51FA;&#x6700;&#x8FD1;&#x5143;&#x7D20;&#x7684;&#x89C4;&#x5219;&#x5339;&#x914D; &#x904D;&#x5386;&#x8C03;&#x7528;body&#x65B9;&#x6CD5;
        List<rule> rules = matches.pop();
        if ((rules != null) && (rules.size() > 0)) {
            String bodyText = this.bodyText.toString();
            for (int i = 0; i < rules.size(); i++) {
                try {
                    Rule rule = rules.get(i);
                    if (debug) {
                        log.debug("  Fire body() for " + rule);
                    }
                    rule.body(namespaceURI, name, bodyText);
                } catch (Exception e) {
                    log.error("Body event threw exception", e);
                    throw createSAXException(e);
                } catch (Error e) {
                    log.error("Body event threw error", e);
                    throw e;
                }
            }
        } else {
            if (debug) {
                log.debug("  No rules found matching '" + match + "'.");
            }
            if (rulesValidation) {
                log.warn("  No rules found matching '" + match + "'.");
            }
        }

        // Recover the body text from the surrounding element
        bodyText = bodyTexts.pop();

        // Fire "end" events for all relevant rules in reverse order
        // &#x904D;&#x5386;&#x89C4;&#x5219;&#x8C03;&#x7528;end&#x65B9;&#x6CD5;
        if (rules != null) {
            for (int i = 0; i < rules.size(); i++) {
                int j = (rules.size() - i) - 1;
                try {
                    Rule rule = rules.get(j);
                    if (debug) {
                        log.debug("  Fire end() for " + rule);
                    }
                    rule.end(namespaceURI, name);
                } catch (Exception e) {
                    log.error("End event threw exception", e);
                    throw createSAXException(e);
                } catch (Error e) {
                    log.error("End event threw error", e);
                    throw e;
                }
            }
        }

        // Recover the previous match expression
        int slash = match.lastIndexOf('/');
        if (slash >= 0) {
            match = match.substring(0, slash);
        } else {
            match = "";
        }

    }

    // &#x5904;&#x7406;&#x5230;&#x8FBE;&#x6587;&#x6863;&#x5F00;&#x5934;&#x7684;&#x901A;&#x77E5;
        @Override
    public void startDocument() throws SAXException {

        if (saxLog.isDebugEnabled()) {
            saxLog.debug("startDocument()");
        }

        if (locator instanceof Locator2) {
            if (root instanceof DocumentProperties.Charset) {
                String enc = ((Locator2) locator).getEncoding();
                if (enc != null) {
                    try {
                        ((DocumentProperties.Charset) root).setCharset(B2CConverter.getCharset(enc));
                    } catch (UnsupportedEncodingException e) {
                        log.warn(sm.getString("disgester.encodingInvalid", enc), e);
                    }
                }
            } else if (root instanceof DocumentProperties.Encoding) {
                ((DocumentProperties.Encoding) root).setEncoding(((Locator2) locator).getEncoding());
            }
        }

        // ensure that the digester is properly configured, as
        // the digester could be used as a SAX ContentHandler
        // rather than via the parse() methods.

        configure();
    }

    // &#x5904;&#x7406;&#x5230;&#x8FBE;&#x6587;&#x6863;&#x672B;&#x5C3E;&#x7684;&#x901A;&#x77E5;
        @Override
    public void endDocument() throws SAXException {

        if (saxLog.isDebugEnabled()) {
            if (getCount() > 1) {
                saxLog.debug("endDocument():  " + getCount() + " elements left");
            } else {
                saxLog.debug("endDocument()");
            }
        }

        // &#x5F39;&#x51FA;stack&#x4E2D;&#x7684;&#x6240;&#x6709;&#x5BF9;&#x8C61;
        while (getCount() > 1) {
            pop();
        }

        // Fire "finish" events for all defined rules
        // &#x904D;&#x5386;&#x6240;&#x6709;&#x7684;&#x89C4;&#x5219; &#x8C03;&#x7528;finish&#x65B9;&#x6CD5;
        for (Rule rule : getRules().rules()) {
            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();

    }

}</rule></rule></string></class<?></object></object></list<rule></stringbuilder>

sax解析xml的过程中无论是处理文档还是节点元素都会有开始解析节点、结束解析节点的通知,会调用子类Digester的相应方法,在方法中用事先定义的规则对节点元素进行事件处理。

接着尝试自定义一个规则来打印节点解析过程中的日志,方便我们更加清晰的明白其处理流程。

自定义规则CustomRule,继承Rule

@Slf4j(topic = "e")
public class CustomRule extends Rule {

  @Override
  public void begin(String namespace, String name, Attributes attributes) throws Exception {
    log.info("&#x5F00;&#x59CB;&#x89E3;&#x6790;" + name + "&#x8282;&#x70B9;");
    log.info("&#x8282;&#x70B9;&#x5C5E;&#x6027;&#x503C;:");
    for (int i = 0; i < attributes.getLength(); i++) {
      log.info(attributes.getLocalName(i) + "=" + attributes.getValue(i));
    }
  }

  @Override
  public void body(String namespace, String name, String text) throws Exception {
    log.info("&#x8282;&#x70B9;&#x5143;&#x7D20;" + name + "&#x5185;&#x5BB9;:" + text);
  }

  @Override
  public void end(String namespace, String name) throws Exception {
    log.info("&#x7ED3;&#x675F;&#x89E3;&#x6790;" + name + "&#x8282;&#x70B9;");
  }
}

在测试类中加入一行代码使我们自定义的规则发生作用, digester.addRule("department/user", new CustomRule());&#xA0;自定义规则匹配user节点,下图可以看到节点解析过程中调用方法传递的参数等信息

Digester解析xml原理

最后附上tomcat中的server.xml解析代码

server.xml

&#xA0;<?xml version="1.0" encoding="UTF-8"?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.

  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

  See the License for the specific language governing permissions and
  limitations under the License.

-->
<!-- Note:  A "Server" is not itself a "Container", so you may not
     define subcomponents such as "Valves" at this level.

     Documentation at /docs/config/server.html
 -->
<server port="8005" shutdown="SHUTDOWN">
  <listener classname="org.apache.catalina.startup.VersionLoggerListener">
  <!-- Security listener. Documentation at /docs/config/listeners.html
  <Listener className="org.apache.catalina.security.SecurityListener" />
  -->
  <!--APR library loader. Documentation at /docs/apr.html -->
  <listener classname="org.apache.catalina.core.AprLifecycleListener" sslengine="on">
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <listener classname="org.apache.catalina.core.JreMemoryLeakPreventionListener">
  <listener classname="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener">
  <listener classname="org.apache.catalina.core.ThreadLocalLeakPreventionListener">

  <!-- Global JNDI resources
       Documentation at /docs/jndi-resources-howto.html
  -->
  <globalnamingresources>
    <!-- Editable user database that can also be used by
         UserDatabaseRealm to authenticate users
    -->
    <resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml">
  </resource></globalnamingresources>

  <!-- A "Service" is a collection of one or more "Connectors" that share
       a single "Container" Note:  A "Service" is not itself a "Container",
       so you may not define subcomponents such as "Valves" at this level.

       Documentation at /docs/config/service.html
   -->
  <service name="Catalina">

    <!--The connectors can use a shared executor, you can define one or more named thread pools-->
    <!--
    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="150" minSpareThreads="4"/>
    -->

    <!-- A "Connector" represents an endpoint by which requests are received
         and responses are returned. Documentation at :
         Java HTTP Connector: /docs/config/http.html
         Java AJP  Connector: /docs/config/ajp.html
         APR (HTTP/AJP) Connector: /docs/apr.html
         Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
    -->
    <connector port="8080" protocol="HTTP/1.1" connectiontimeout="20000" redirectport="8443">
    <!-- A "Connector" using the shared thread pool-->
    <!--
    <Connector executor="tomcatThreadPool"
               port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    -->
    <!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443
         This connector uses the NIO implementation. The default
         SSLImplementation will depend on the presence of the APR/native
         library and the useOpenSSL attribute of the
         AprLifecycleListener.

         Either JSSE or OpenSSL style configuration may be used regardless of
         the SSLImplementation selected. JSSE style configuration is used below.

    -->
    <!--
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="150" SSLEnabled="true">
        <SSLHostConfig>
            <Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
                         type="RSA" />
        </SSLHostConfig>
    </Connector>
    -->
    <!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2
         This connector uses the APR/native implementation which always uses
         OpenSSL for TLS.

         Either JSSE or OpenSSL style configuration may be used. OpenSSL style
         configuration is used below.

    -->
    <!--
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
               maxThreads="150" SSLEnabled="true" >
        <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
        <SSLHostConfig>
            <Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
                         certificateFile="conf/localhost-rsa-cert.pem"
                         certificateChainFile="conf/localhost-rsa-chain.pem"
                         type="RSA" />
        </SSLHostConfig>
    </Connector>
    -->

    <!-- Define an AJP 1.3 Connector on port 8009 -->

    <connector protocol="AJP/1.3" address="::1" port="8009" redirectport="8443">

    <!-- An Engine represents the entry point (within Catalina) that processes
         every request.  The Engine implementation for Tomcat stand alone
         analyzes the HTTP headers included with the request, and passes them
         on to the appropriate Host (virtual host).

         Documentation at /docs/config/engine.html -->

    <!-- You should set jvmRoute to support load-balancing via AJP ie :
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
    -->
    <engine name="Catalina" defaulthost="localhost">

      <!--For clustering, please take a look at documentation at:
          /docs/cluster-howto.html  (simple how to)
          /docs/config/cluster.html (reference documentation) -->
      <!--
      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
      -->

      <!-- Use the LockOutRealm to prevent attempts to guess user passwords
           via a brute-force attack -->
      <realm classname="org.apache.catalina.realm.LockOutRealm">
        <!-- This Realm uses the UserDatabase configured in the global JNDI
             resources under the key "UserDatabase".  Any edits
             that are performed against this UserDatabase are immediately
             available for use by the Realm.  -->
        <realm classname="org.apache.catalina.realm.UserDatabaseRealm" resourcename="UserDatabase">
      </realm>

      <host name="localhost" appbase="webapps" unpackwars="true" autodeploy="true">

        <context path docbase="../webapps/zxq/" debug="0">

        <!-- SingleSignOn valve, share authentication between web applications
             Documentation at: /docs/config/valve.html -->
        <!--
        <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
        -->

        <!-- Access log processes all example.

             Documentation at: /docs/config/valve.html
             Note: The pattern used is equivalent to using pattern="common" -->
        <valve classname="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b">

      </valve></context></host>
    </realm></engine>
  </connector></connector></service>
</listener></listener></listener></listener></listener></server>

org.apache.catalina.startup.Catalina#createStartDigester

&#xA0;    /**
     * Create and configure the Digester we will be using for startup.

     * @return the main digester to parse server.xml
     */
    protected Digester createStartDigester() {
        long t1=System.currentTimeMillis();
        // Initialize the digester
        Digester digester = new Digester();
        digester.setValidating(false);
        digester.setRulesValidation(true);
        Map<class<?>, List<string>> fakeAttributes = new HashMap<>();
        List<string> objectAttrs = new ArrayList<>();
        objectAttrs.add("className");
        fakeAttributes.put(Object.class, objectAttrs);
        // Ignore attribute added by Eclipse for its internal tracking
        List<string> contextAttrs = new ArrayList<>();
        contextAttrs.add("source");
        fakeAttributes.put(StandardContext.class, contextAttrs);
        digester.setFakeAttributes(fakeAttributes);
        digester.setUseContextClassLoader(true);

        // Configure the actions we will be using
        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.NamingResourcesImpl");
        digester.addSetProperties("Server/GlobalNamingResources");
        digester.addSetNext("Server/GlobalNamingResources",
                            "setGlobalNamingResources",
                            "org.apache.catalina.deploy.NamingResourcesImpl");

        digester.addObjectCreate("Server/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Listener");
        digester.addSetNext("Server/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        digester.addObjectCreate("Server/Service",
                                 "org.apache.catalina.core.StandardService",
                                 "className");
        digester.addSetProperties("Server/Service");
        digester.addSetNext("Server/Service",
                            "addService",
                            "org.apache.catalina.Service");

        digester.addObjectCreate("Server/Service/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Service/Listener");
        digester.addSetNext("Server/Service/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        //Executor
        digester.addObjectCreate("Server/Service/Executor",
                         "org.apache.catalina.core.StandardThreadExecutor",
                         "className");
        digester.addSetProperties("Server/Service/Executor");

        digester.addSetNext("Server/Service/Executor",
                            "addExecutor",
                            "org.apache.catalina.Executor");

        digester.addRule("Server/Service/Connector",
                         new ConnectorCreateRule());
        digester.addRule("Server/Service/Connector",
                         new SetAllPropertiesRule(new String[]{"executor", "sslImplementationName"}));
        digester.addSetNext("Server/Service/Connector",
                            "addConnector",
                            "org.apache.catalina.connector.Connector");

        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig",
                                 "org.apache.tomcat.util.net.SSLHostConfig");
        digester.addSetProperties("Server/Service/Connector/SSLHostConfig");
        digester.addSetNext("Server/Service/Connector/SSLHostConfig",
                "addSslHostConfig",
                "org.apache.tomcat.util.net.SSLHostConfig");

        digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
                         new CertificateCreateRule());
        digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
                         new SetAllPropertiesRule(new String[]{"type"}));
        digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate",
                            "addCertificate",
                            "org.apache.tomcat.util.net.SSLHostConfigCertificate");

        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
                                 "org.apache.tomcat.util.net.openssl.OpenSSLConf");
        digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf");
        digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
                            "setOpenSslConf",
                            "org.apache.tomcat.util.net.openssl.OpenSSLConf");

        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
                                 "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
        digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd");
        digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
                            "addCmd",
                            "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");

        digester.addObjectCreate("Server/Service/Connector/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Service/Connector/Listener");
        digester.addSetNext("Server/Service/Connector/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",
                                  null, // MUST be specified in the element
                                  "className");
        digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");
        digester.addSetNext("Server/Service/Connector/UpgradeProtocol",
                            "addUpgradeProtocol",
                            "org.apache.coyote.UpgradeProtocol");

        // 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/"));
        addClusterRuleSet(digester, "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));
        addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");

        long t2=System.currentTimeMillis();
        if (log.isDebugEnabled()) {
            log.debug("Digester for server.xml created " + ( t2-t1 ));
        }
        return (digester);

    }</string></string></string></class<?>

Original: https://www.cnblogs.com/monianxd/p/16588627.html
Author: 默念x
Title: Digester解析xml原理

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/620041/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 第16章 变量、流程控制与游标

    第16章 变量、流程控制与游标 1. 变量 在MySQL数据库的存储过程和函数中,可以使用变量来存储查询或计算的中间结果数据,或者输出最终的结果数据。 在 MySQL 数据库中,变…

    数据库 2023年6月6日
    083
  • Go 接口:深入内部原理

    接口的基本概念不在这里赘述,详情请看第十六章:接口 nil 非空? package main func main() { var obj interface{} obj = 1 p…

    数据库 2023年6月6日
    097
  • Dubbo源码(三)-服务导出(生产者)

    前言 本文基于Dubbo2.6.x版本,中文注释版源码已上传github:xiaoguyu/dubbo 在了解了Dubbo SPI后,我们来了解下Dubbo服务导出的过程。 Dub…

    数据库 2023年6月11日
    081
  • 【01】Spring Boot配置文件相关

    1、Spring Boot 获取属性的属性源,优先级从高到低 (1)命令行参数 (2)java:comp/env里的JNDI属性 (3)JVM系统属性 (4)操作系统的环境变量 (…

    数据库 2023年6月9日
    061
  • Linux

    1、关机命令 命令 说明 sync 将数据由内存同步到硬盘中 shutdown 关机 shutdown -h 10 10分钟后关机 shutdown -h now 立马关机 shu…

    数据库 2023年6月16日
    099
  • 2 Java中 == 和 equals 和 hashCode 的区别

    ==是一个比较运算符; 若比较的是基本数据类型,则比较的是值; 若比较的是引用数据类型,则比较的是它们在内存中的内存地址。 说明:对象是存放在堆中,栈中存放的是对象的引用,因此==…

    数据库 2023年6月6日
    089
  • 近年来我带队开发出的一坨屎山

    有人说: 烂代码跟一坨屎一样,很多时候就是和一坨屎共处千万别深挖,说不定把哪里挖塌了把你埋了,扔一坨代码到屎山上,达到自己目的,能跑就行了,你还要搞清楚山上的屎哪一坨是谁拉的,拉的…

    数据库 2023年6月9日
    089
  • 系统稳定性—OutOfMemoryError常见原因及解决方法

    当 JVM内存严重不足时,就会抛出 java.lang.OutOfMemoryError错误。本文总结了常见的 OOM原因及其解决方法,如下图所示。如有遗漏或错误,欢迎补充指正。 …

    数据库 2023年6月11日
    089
  • Ajax

    AJAX(Asynchronous Javascript And Xml) 传统请求及缺点 传统的请求都有哪些? 直接在浏览器地址栏上输入URL。 点击超链接 提交form表单 使…

    数据库 2023年6月14日
    092
  • MySQL启动报:[ERROR] The server quit without updating PID file

    修改配置后 MySQL启动不了,报错: 看见这个不要惊慌,先把刚才修改的配置注释掉,看是不是配置有误!大部分是手误造成。 如果不行,再尝试一下方法: 解决方法 : 给予权限,执行 …

    数据库 2023年6月14日
    077
  • 解决在laravel中执行migrate数据库迁移时抛出的Syntax error or access violation: 1071 Specified key was too long; max key length is 1000 bytes相关的错误问题

    先说解决方法: 在laravel的app/Providers/AppServiceProvider.php中boot方法内,加入长度限制: 至于为什么是248,那是因为在larav…

    数据库 2023年6月14日
    089
  • java读写锁

    工作遇到了金钱计算,需要用到读写锁保证数据安全。记录一下。 单纯读没有限制,读写、写写的时候会有安全问题。 _hashMap_存在并发线程安全问题,而 _hashtable_线程安…

    数据库 2023年6月16日
    070
  • 获取字典中values值中最大的数,返回对应的keys

    1.字典中键值对的获取 print(data.values()) # 查看字典的值 print(data.keys()) # 查看字典的key 2.对字典中的值进行排序 sorte…

    数据库 2023年6月16日
    064
  • leetcode 437. Path Sum III 路径总和 III(中等)

    一、题目大意 给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始,也…

    数据库 2023年6月16日
    086
  • FastDFS安装和简介详细总结

    1、fastDFS简介 1 FastDFS是用c语言编写的一款开源的分布式文件系统。 2 FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用…

    数据库 2023年6月14日
    0105
  • Linux常用指令

    Linux常用指令 到达底部 运行级别有以下几种 0级:关机 1级:单用户 (找回密码) 2级: 多用户没有网络服务 3级: 多用户有网络服务 4级: 系统未使用保留给用户 5级:…

    数据库 2023年6月16日
    081
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球