tomcat源码解析(二)——xml解析过程分析

本文深入解析Tomcat如何利用Digester和SAX解析server.xml。介绍SAX事件驱动的xml解析原理,重点分析Digester在解析过程中的关键回调方法,包括startElement、endElement等,并探讨了属性替换和rule处理流程。
摘要由CSDN通过智能技术生成

阅读tomcat源码过程中,一定会遇到利用Digester解析xml的过程,而这部分对于刚开始研究源码的人来说并不容易理解。在我之前的文章中,也遗留了关于tomcat启动过程中解析server.xml的过程。在这篇文章中,我将解读tomcat启动时解析server.xml的过程来帮助更好的理解Digester的使用和源码。

首先,理解Digester首先需要理解SAX的使用,关于SAX相关的类在jre中rt.jar中,属于jdk自带且可以用于解析xml。SAX是一种事件方式驱动去进行解析xml,它通过逐行读取xml文件,并在解析各个部分时触发相应的事件,然后通过回调函数进行自己的事件处理。与DOM的方式不同,SAX解析的xml不会保存其结构和状态,没有前后父子的关系等。在tomcat的使用中,更多关注的是当读取一个属性节点时对于回调事件的触发,实现相应的类初始化操作。

SAX API中主要有四种处理事件的接口,它们分别是ContentHandler,DTDHandler, EntityResolver 和 ErrorHandler;对应的示例代码如下

            SAXParserFactory factory = SAXParserFactory.newInstance();

            SAXParser saxParser = factory.newSAXParser();

            File file = new File("D:/tomcat/conf/server.xml");

            InputSource input = new InputSource(new FileInputStream(file));

            XMLReader reader = saxParser.getXMLReader();
            
            reader.setContentHandler(new UserContentHandler());
            reader.setDTDHandler(new UserDTDHandler());
            reader.setEntityResolver(new UserEntityResolver());
            reader.setErrorHandler(new UserErrorHandler());
            
            
            reader.parse(input);
通过SAXParserFactory.newInstance()进行实例化,获取SAXParser,并得到XMLReader;

在XMLReader中可以为自己需要监听的事件添加实现。

上面的UserContentHandler,UserDTDHandler,UserEntityResolver,UserErrorHandler分别实现了ContentHandlerDTDHandlerEntityResolverErrorHandler接口;

一般使用较多的是ContentHandler接口的setDocumentLocator、startDocument、endDocument、startElement及endElement、characters函数;会对应到读取文档,结束读取文档,读取节点等的触发事件。

关于更多使用SAX的细节,各位可以自己查看SAX源码或者API文档进行了解。


现在,我将结合tomcat解析server.xml的源码分析解析过程。

首先,我通过tomcat的Catalina类load()中digester.parse(inputSource)跟踪源码,查看其中的细节。

tomcat源码:

Digester的parse;

public Object parse(InputSource input) throws IOException, SAXException {

        configure();
        getXMLReader().parse(input);
        return (root);

    }
其中实际上是调用getXMLReader()的parse方法实现文件加载,重点看下getXMLReader(),其源码如下:

    /**
     * Return the XMLReader to be used for parsing the input document.
     *
     * FIX ME: there is a bug in JAXP/XERCES that prevent the use of a
     * parser that contains a schema with a DTD.
     * @exception SAXException if no XMLReader can be instantiated
     */
    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;
    }
到这里就可以发现它与这篇文章第一部分代码有相似之处

reader.setContentHandler(new UserContentHandler());
reader.setDTDHandler(new UserDTDHandler());
reader.setEntityResolver(new UserEntityResolver());
reader.setErrorHandler(new UserErrorHandler());

只是其中的参数变成了this。


现在重新查看Digester类的继承关系,如下图,可以发现Digester实际上已经在祖父类DefaultHandler中实现了SAX API中主要四种处理事件接口


所以当参数传入this后,回调函数实际上就是调用自身实现的SAX API接口方法。


现在问题来了,四个接口大概二十多个回调方法,我们没有精力全部跟踪,到底server.xml加载过程中到底触发了哪些事件呢?

现在我根据源码,写了一段测试代码,单独先加载server.xml,查看调用了哪些函数。具体源码如下(代码中的路径是我tomcat的server.xml的绝对路径):

package com.zpf.saxparserfactory.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.LexicalHandler;

public class Test {

    public static void main(String[] args) {
        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();

            SAXParser saxParser = factory.newSAXParser();

            File file = new File("D:/tomcat/conf/server.xml");

            InputSource input = new InputSource(new FileInputStream(file));

            XMLReader reader = saxParser.getXMLReader();
            
            reader.setContentHandler(new UserContentHandler());
            reader.setDTDHandler(new UserDTDHandler());
            reader.setEntityResolver(new UserEntityResolver());
            reader.setErrorHandler(new UserErrorHandler());
            reader.setProperty(
                    "http://xml.org/sax/properties/lexical-handler", new UserLexicalHandler());
            
            reader.parse(input);
            
        } catch (IOException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        }
    }

    private static class UserLexicalHandler implements LexicalHandler{

        @Override
        public void startDTD(String name, String publicId, String systemId) throws SAXException {
            System.out.println("startDTD:" + publicId + "---" + systemId + "---" + name);
            
        }

        @Override
        public void endDTD() throws SAXException {
            System.out.println("endDTD:");
            
        }

        @Override
        public void startEntity(String name) throws SAXException {
            System.out.println("startEntity:" + name);
            
        }

        @Override
        public void endEntity(String name) throws SAXException {
            System.out.println("endEntity:" + name);
            
        }

        @Override
        public void startCDATA() throws SAXException {
            System.out.println("startCDATA:" );
            
        }

        @Override
        public void endCDATA() throws SAXException {
            System.out.println("endCDATA:" );
            
        }

        @Override
        public void comment(char[] ch, int start, int length) throws SAXException {
            //System.out.println("comment:" + new String(ch,start,length));//Digester的comment
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值