博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Tommcat源码学习(四)--Tomcat_7.0.70 server.xml文件的加载与解析
阅读量:6769 次
发布时间:2019-06-26

本文共 30822 字,大约阅读时间需要 102 分钟。

hot3.png

1、文件的加载

Bootstrap的load方法是加载Tomcat的server.xml的入口,load方法实际通过反射调用catalinaDaemon(类型为Catalina)的load方法:

/** * Load daemon. */private void load(String[] arguments)    throws Exception {    // Call the load() method    String methodName = "load";    Object param[];    Class
paramTypes[]; if (arguments==null || arguments.length==0) { paramTypes = null; param = null; } else { paramTypes = new Class[1]; paramTypes[0] = arguments.getClass(); param = new Object[1]; param[0] = arguments; } Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes);//通过反射机制调用Catalina类的load方法。 if (log.isDebugEnabled()) log.debug("Calling startup class " + method); method.invoke(catalinaDaemon, param);}

Catalina类的load方法:

/* * Load using arguments */public void load(String args[]) {    try {        if (arguments(args)) {            load();//调用自身的load方法        }    } catch (Exception e) {        e.printStackTrace(System.out);    }} /** * Start a new server instance. */public void load() {    long t1 = System.nanoTime();    initDirs(); //用于对catalina.home和catalina.base的一些检查工作    // Before digester - it may be needed    initNaming();//给系统设置java.naming.factory.url.pkgs和java.naming.factory.initial    // Create and execute our Digester    Digester digester = createStartDigester();//实例化Digester    InputSource inputSource = null;    InputStream inputStream = null;    File file = null;    try {        try {            file = configFile();//获取conf/server.xml配置文件            inputStream = new FileInputStream(file);获取conf/server.xml配置文件输入流            inputSource = new InputSource(file.toURI().toURL().toString());        } catch (Exception e) {            if (log.isDebugEnabled()) {                log.debug(sm.getString("catalina.configFail", file), e);            }        }        if (inputStream == null) {            try {                inputStream = getClass().getClassLoader()                    .getResourceAsStream(getConfigFile());                inputSource = new InputSource                    (getClass().getClassLoader()                     .getResource(getConfigFile()).toString());            } catch (Exception e) {                if (log.isDebugEnabled()) {                    log.debug(sm.getString("catalina.configFail",                            getConfigFile()), e);                }            }        }        // This should be included in catalina.jar        // Alternative: don't bother with xml, just create it manually.        if( inputStream==null ) {            try {                inputStream = getClass().getClassLoader()                        .getResourceAsStream("server-embed.xml");                inputSource = new InputSource                (getClass().getClassLoader()                        .getResource("server-embed.xml").toString());            } catch (Exception e) {                if (log.isDebugEnabled()) {                    log.debug(sm.getString("catalina.configFail",                            "server-embed.xml"), e);                }            }        }        if (inputStream == null || inputSource == null) {            if  (file == null) {                log.warn(sm.getString("catalina.configFail",                        getConfigFile() + "] or [server-embed.xml]"));            } else {                log.warn(sm.getString("catalina.configFail",                        file.getAbsolutePath()));                if (file.exists() && !file.canRead()) {                    log.warn("Permissions incorrect, read permission is not allowed on the file.");                }            }            return;        }        try {            inputSource.setByteStream(inputStream);//将FileInputStream封装为InputSource            digester.push(this);            digester.parse(inputSource);//调用Digester的parse方法进行解析        } catch (SAXParseException spe) {            log.warn("Catalina.start using " + getConfigFile() + ": " +                    spe.getMessage());            return;        } catch (Exception e) {            log.warn("Catalina.start using " + getConfigFile() + ": " , e);            return;        }    } finally {        if (inputStream != null) {            try {                inputStream.close();            } catch (IOException e) {                // Ignore            }        }    }    getServer().setCatalina(this);    // Stream redirection    initStreams();//initStreams对输出流、错误流重定向    // Start the new server    try {        getServer().init();//初始化server    } catch (LifecycleException e) {        if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {            throw new java.lang.Error(e);        } else {            log.error("Catalina.start", e);        }    }    long t2 = System.nanoTime();    if(log.isInfoEnabled()) {        log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");    }}

下面开始分析整个过程:

1.1、initDirs()方法用于对catalina.home和catalina.base的一些检查工作

protected void initDirs() {    String catalinaHome = System.getProperty(Globals.CATALINA_HOME_PROP);    if (catalinaHome == null) {        // Backwards compatibility patch for J2EE RI 1.3        String j2eeHome = System.getProperty("com.sun.enterprise.home");        if (j2eeHome != null) {            catalinaHome=System.getProperty("com.sun.enterprise.home");        } else if (System.getProperty(Globals.CATALINA_BASE_PROP) != null) {            catalinaHome = System.getProperty(Globals.CATALINA_BASE_PROP);        }    }    // last resort - for minimal/embedded cases.    if(catalinaHome==null) {        catalinaHome=System.getProperty("user.dir");    }    if (catalinaHome != null) {        File home = new File(catalinaHome);        if (!home.isAbsolute()) {            try {                catalinaHome = home.getCanonicalPath();            } catch (IOException e) {                catalinaHome = home.getAbsolutePath();            }        }        System.setProperty(Globals.CATALINA_HOME_PROP, catalinaHome);    }    if (System.getProperty(Globals.CATALINA_BASE_PROP) == null) {        System.setProperty(Globals.CATALINA_BASE_PROP,                           catalinaHome);    } else {        String catalinaBase = System.getProperty(Globals.CATALINA_BASE_PROP);        File base = new File(catalinaBase);        if (!base.isAbsolute()) {            try {                catalinaBase = base.getCanonicalPath();            } catch (IOException e) {                catalinaBase = base.getAbsolutePath();            }        }        System.setProperty(Globals.CATALINA_BASE_PROP, catalinaBase);    }    String temp = System.getProperty("java.io.tmpdir");    if (temp == null || (!(new File(temp)).exists())            || (!(new File(temp)).isDirectory())) {        log.error(sm.getString("embedded.notmp", temp));    }}

1.2、initNaming()方法给系统设置java.naming.factory.url.pkgs和java.naming.factory.initial

protected void initNaming() {    // Setting additional variables    if (!useNaming) {        log.info( "Catalina naming disabled");        System.setProperty("catalina.useNaming", "false");    } else {        System.setProperty("catalina.useNaming", "true");        String value = "org.apache.naming";        String oldValue =            System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);        if (oldValue != null) {            value = value + ":" + oldValue;        }        System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);        if( log.isDebugEnabled() ) {            log.debug("Setting naming prefix=" + value);        }        value = System.getProperty            (javax.naming.Context.INITIAL_CONTEXT_FACTORY);        if (value == null) {            System.setProperty                (javax.naming.Context.INITIAL_CONTEXT_FACTORY,                 "org.apache.naming.java.javaURLContextFactory");        } else {            log.debug( "INITIAL_CONTEXT_FACTORY already set " + value );        }    }}

在创建JNDI上下文时,使用Context.INITIAL _ CONTEXT _ FACTORY("java.naming.factory.initial")属性,来指定创建JNDI上下文的工厂类;Context.URL _ PKG _ PREFIXES("java.naming.factory.url.pkgs")用在查询url中包括scheme方法id时创建对应的JNDI上下文.

1.3、createStartDigester()创建并配置将要用来启动的Digester实例,并且设置一些列Rule,具体映射到server.xml

/** * Create and configure the Digester we will be using for startup. */protected Digester createStartDigester() {    long t1=System.currentTimeMillis();    // Initialize the digester    Digester digester = new Digester();    digester.setValidating(false);    digester.setRulesValidation(true);    HashMap
, List
> fakeAttributes = new HashMap
, List
>(); ArrayList
attrs = new ArrayList
(); attrs.add("className"); fakeAttributes.put(Object.class, attrs); 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.NamingResources"); digester.addSetProperties("Server/GlobalNamingResources"); digester.addSetNext("Server/GlobalNamingResources", "setGlobalNamingResources", "org.apache.catalina.deploy.NamingResources"); 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"})); digester.addSetNext("Server/Service/Connector", "addConnector", "org.apache.catalina.connector.Connector"); 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"); // 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);}

从上面的代码可以看出:首先创建Digester,Digester继承了DefaultHandler,而DefaultHandler默认实现了ContentHander、DTDHander、ErrorHandler及EntityResolver 这4个接口,代码如下:

public class DefaultHandler implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler

通过源码可以发现DefaultHandler的所有实现都是空实现,所以解析还需要Digester。(具体分析后面在说)。

1.4、configFile()获取配置文件conf/server.xml,并使用FileInputStream获取conf/server.xml配置文件输入流

/** * Return a File object representing our configuration file. */protected File configFile() {    File file = new File(configFile);    if (!file.isAbsolute()) {        file = new File(System.getProperty(Globals.CATALINA_BASE_PROP), configFile);    }    return (file);}

1.5、将FileInputStream封装为InputSource,并且调用Digester的parse方法进行解析

inputSource.setByteStream(inputStream);digester.push(this);digester.parse(inputSource);

1.6、initStreams()对输出流、错误流重定向

protected void initStreams() {    // Replace System.out and System.err with a custom PrintStream    System.setOut(new SystemLogHandler(System.out));    System.setErr(new SystemLogHandler(System.err));}

1.7、初始化server

// Start the new server    try {        getServer().init();    } catch (LifecycleException e) {        if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {            throw new java.lang.Error(e);        } else {            log.error("Catalina.start", e);        }    }

2、文件的解析

当加载server.xml配置文件到内存后,开始对XML文件中的内容进行解析,主要包含两个步骤:

  1. 构造server.xml的规则,这些规则即可以用于构造Tomcat内部的容器(如StandardServer,StandardService等),也可以对server.xml进行合法性检查。如果server.xml不符合Tomcat内置的规则,在解析时将抛出异常,进而导致Tomcat无法启动。
  2. 使用SAX解析server.xml,边解析边应用规则,最终使用server.xml中的配置构建好Tomcat所需的各种容器。

Tomcat将server.xml文件中的所有元素上的属性都抽象为Rule,以Server元素为例,在内存中对应Server实例,Server实例的属性值就来自于Server元素的属性值。通过对规则(Rule)的应用,最终改变Server实例的属性值。Rule是一个抽象类,其中定义了以下方法:

  • getDigester:获取Digester实例;
  • setDigester:设置Digester实例;
  • getNamespaceURI:获取Rule所在的相对命名空间URI;
  • setNamespaceURI:设置Rule所在的相对命名空间URI;
  • begin(String namespace, String name, Attributes attributes):此方法在遇到一个匹配的XML元素的开头时被调用,如<Server>;
  • body(String namespace, String name, String text):在遇到匹配XML元素的body时,此方法被调用,如进入标签内部时;
  • end(String namespace, String name):此方法在遇到一个匹配的XML元素的末尾时被调用。如:< /Server>;

Rule目前有很多实现类,如:NodeCreateRule、AbsoluteOrderingRule、CallParamRule、ConnectorCreateRule等。下图展示了Rule的部分实现类:

Tomcat使用SAX(Simple API for XML)解析XML:

SAX解析XML采用的是从上而下的基于事件驱动的解析方式,在解析过程中会视情况自动调用ContentHandler接口中的startDocument()、startElement()、characters()、endElement()、endDocument()等相关的方法。

由编译执行的结果分析:

  • startDocument()方法只会在文档开始解析的时候被调用,每次解析只会调用一次。
  • startElement()方法每次在开始解析一个元素,即遇到元素标签开始的时候都会调用。
  • characters()方法也是在每次解析到元素标签携带的内容时都会调用,即使该元素标签的内容为空或换行。而且如果元素内嵌套元素,在父元素结束标签前, characters()方法会再次被调用,此处需要注意。
  • endElement()方法每次在结束解析一个元素,即遇到元素标签结束的时候都会调用。
  • endDocument()方法只会在文档解析结束的时候被调用,每次解析只会调用一次。

使用SAX解析XML的好处:

  • SAX 不用解析完整个文档
  • 相比于 DOM 而言 SAX 是一种速度更快,更有效,占用内存更少的解析 XML 文件的方法
  • 逐行扫描,可以做到边扫描边解析,因此 SAX 可以在解析文档的任意时刻停止解析

由于SAX是基于事件驱动的,不用解析完整个文档,在按内容顺序解析文档过程中, SAX 会判断当前读到的字符是否符合 XML 文件语法中的某部分。如果符合某部分,则会触发事件。所谓触发事件,就是调用一些回调方法。在用 SAX 解析 xml 文档时候,在读取到文档开始和结束标签时候就会回调一个事件,在读取到其他节点与内容时候也会回调一个事件。在 SAX 接口中,事件源是 org.xml.sax 包中的 XMLReader ,它通过 parser() 方法来解析 XML 文档,并产生事件。事件处理器是 org.xml.sax 包中 ContentHander 、 DTDHander 、 ErrorHandler ,以及 EntityResolver 这4个接口:

  1. ContentHander ( XML 文档的开始与结束)setContentHandler(ContentHandler h)
  2. DTDHander ( 处理 DTD 解析) setDTDHandler(DTDHandler h)
  3. ErrorHandler ( 处理 XML 时产生的错误)setErrorHandler(ErrorHandler h)
  4. EntityResolver (处理外部实体) setEntityResolver(EntityResolver e)

回调方法一般都定义在ContentHandler接口中,上面已经对这些回调方法的加载顺序已经说了就不在介绍了。

使用 SAX 解析 XML 文件一般有以下五个步骤:

  1. 创建一个 SAXParserFactory 对象;
  2. 调用 SAXParserFactory 中的 newSAXParser 方法创建一个 SAXParser 对象;
  3. 然后在调用 SAXParser 中的 getXMLReader 方法获取一个 XMLReader 对象;
  4. 实例化一个 DefaultHandler 对象;
  5. 连接事件源对象 XMLReader 到事件处理类 DefaultHandler 中;
  6. 调用 XMLReader 的 parse 方法从输入源中获取到的 xml 数据;
  7. 通过 DefaultHandler 返回我们需要的数据集合。

我们通过源码可以发现DefaultHandler的所有实现都是空实现,所以解析还需要Digester自身,代码如下:

@Overridepublic void startDocument() throws SAXException {    if (saxLog.isDebugEnabled()) {        saxLog.debug("startDocument()");    }    // ensure that the digester is properly configured, as     // the digester could be used as a SAX ContentHandler    // rather than via the parse() methods.    configure();}@Overridepublic 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);    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    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    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 = 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 + "'."); } }}@Overridepublic 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 = 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();}@Overridepublic 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 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 List
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 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 = ""; }}

当我们创建好Digester后,会调用addObjectCreate、addSetProperties、addSetNext方法陆续添加很多Rule,这些方法的实现如代码:

public void addObjectCreate(String pattern, String className,                            String attributeName) {    addRule(pattern,            new ObjectCreateRule(className, attributeName));} public void addSetProperties(String pattern) {    addRule(pattern,            new SetPropertiesRule());}public void addSetNext(String pattern, String methodName,                       String paramType) {    addRule(pattern,            new SetNextRule(methodName, paramType));}

这三个方法分别创建ObjectCreateRule、SetPropertiesRule及SetNextRule,为了便于理解,我们举例说明(Server标签):

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

我们知道最终会创建ObjectCreateRule、SetPropertiesRule及SetNextRule,并且调用addRule方法。

public void addRule(String pattern, Rule rule) {    rule.setDigester(this);    getRules().add(pattern, rule);}

从代码可以看出,addRule方法首先调用getRules方法获取RulesBase,然后调用RulesBase的add方法。代码如下:

//getRules()获取RulesBasepublic Rules getRules() {    if (this.rules == null) {        this.rules = new RulesBase();        this.rules.setDigester(this);    }    return (this.rules);}//RulesBase的add方法@Overridepublic 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 = 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); }}

其中,cache的数据结构为HashMap<String,List<Rule>>,每个键值维护一个List<Rule>,由此可知,对Server标签来说,对应的Rule列表为ObjectCreateRule、SetPropertiesRule及SetNextRule。

Digester解析XML的入口是其parse方法,其处理步骤如下:

1.创建XMLReader ;

2.使用XMLReader解析XML。

public Object parse(InputSource input) throws IOException, SAXException {    configure();    getXMLReader().parse(input);    return (root);}//configure()protected void configure() {    // Do not configure more than once    if (configured) {        return;    }    log = LogFactory.getLog("org.apache.tomcat.util.digester.Digester");    saxLog = LogFactory.getLog("org.apache.tomcat.util.digester.Digester.sax");    // Perform lazy configuration as needed    initialize(); // call hook method for subclasses that want to be initialized once only    // Nothing else required by default    // Set the configuration flag to avoid repeating    configured = true;}

getXMLReader方法调用getParser创建SAXParser ,然后调用SAXParser 的getXMLReader方法创建XMLReader.

public XMLReader getXMLReader() throws SAXException {    if (reader == null){        reader = getParser().getXMLReader();    }                                       reader.setDTDHandler(this);               reader.setContentHandler(this);                if (entityResolver == null){        reader.setEntityResolver(this);    } else {        reader.setEntityResolver(entityResolver);               }        reader.setProperty(            "http://xml.org/sax/properties/lexical-handler", this);    reader.setErrorHandler(this);    return reader;}

getParser方法调用getFactory方法创建SAXParserFactory,然后调用SAXParserFactory的newSAXParser方法创建SAXParser

public SAXParser getParser() {    // Return the parser we already created (if any)    if (parser != null) {        return (parser);    }    // Create a new parser    try {        parser = getFactory().newSAXParser();    } catch (Exception e) {        log.error("Digester.getParser: ", e);        return (null);    }    return (parser);}

getFactory方法使用SAX的API生成SAXParserFactory实例.

public SAXParserFactory getFactory() throws SAXNotRecognizedException, SAXNotSupportedException,ParserConfigurationException {    if (factory == null) {        factory = SAXParserFactory.newInstance();        factory.setNamespaceAware(namespaceAware);        // Preserve xmlns attributes        if (namespaceAware) {            factory.setFeature(                    "http://xml.org/sax/features/namespace-prefixes",                    true);        }        factory.setValidating(validating);        if (validating) {            // Enable DTD validation            factory.setFeature(                    "http://xml.org/sax/features/validation",                    true);            // Enable schema validation            factory.setFeature(                    "http://apache.org/xml/features/validation/schema",                    true);        }    }    return (factory);}

XMLReader解析XML时,会生成事件,回调Digester的startDocument方法,解析的第一个元素是Server,此时回调Digester的startElement方法,入参Attributes list即Server上的属性,如port、shutdown等,入参qName即为Server.

@Overridepublic 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);    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    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    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 = 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 + "'."); } }}

startElement方法的处理步骤如下:

1.match刚开始为空字符串,拼接Server后变为Server。

2.调用RulesBase的match方法,返回cache中按照键值Server匹配的ObjectCreateRule、SetPropertiesRule及SetNextRule。

3.循环列表依次遍历ObjectCreateRule、SetPropertiesRule及SetNextRule,并调用它们的begin方法。

ObjectCreateRule的begin方法将生成Server的实例(默认为"org.apache.catalina.core.StandardServer",用户可以通过给Server标签指定className使用其它Server实现),最后将Server的实例压入Digester的栈中.

@Overridepublic 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);}

SetPropertiesRule的begin方法首先将刚才压入栈中的Server实例出栈,然后给Server实例设置各个属性值,如port、shutdown等:

@Overridepublic void begin(String namespace, String theName, 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

SetNextRule的begin不做什么动作。当遇到Server的结束标签时,还会依次调用ObjectCreateRule、SetPropertiesRule及SetNextRule的end方法,不再赘述。所有元素的解析都与Server标签同理,最终将server.xml文件中设置的元素及其属性值,构造出tomcat中的容器,如:Server、Service、Connector等.

转载于:https://my.oschina.net/zhengweishan/blog/706989

你可能感兴趣的文章
js Object.create 初探
查看>>
增量更新
查看>>
js 原型链 prototype __proto__
查看>>
Could not find com.android.tools.build:aapt2:3.2.0-alpha14-4748712.
查看>>
MS BizSpark计划-免费提供软件和服务
查看>>
『转载』NetBeans开发J2ME手机程序之——文件浏览器
查看>>
软件测试之魂:核心测试设计精解(第2版)
查看>>
“.NET技术”使用WCF实现SOA面向服务编程—— 架构设计
查看>>
C# (事件触发)回调函数,完美处理各类疑难杂症!
查看>>
SRM524DIV2总结
查看>>
AjaxFileUpload + *.ashx 文件上传在IE8.0(XP,VS2010,Development Server)下的注意
查看>>
LDD3 读书笔记 之 第 2 章 建立和运行模块
查看>>
使用XMLHttpRequest来获取xml文档
查看>>
oracle 的服务器进程(PMON, SMON,CKPT,DBWn,LGWR,ARCn)
查看>>
CreateThread、_beginthreadex和AfxBeginThread 的区别
查看>>
营销方法论
查看>>
【转】深入PHP FTP类的详解
查看>>
PHP查找中文字符的解决方案
查看>>
css代码中position的定位,baidu+总结
查看>>
取得Access自增标识字段在插入数据后的id值
查看>>