zoukankan      html  css  js  c++  java
  • 【Tomcat8源码学习之五】Digester

    Tomcat源码版本:apache-tomcat-8.5.54-src
    JDK源码版本:jdk1.8.0_171

    org.apache.tomcat.util.digester.Digester用于解析server.xml,通过它可以很方便的从xml文件生成java对象。
    1、Java解析XML文件主要有两个方式
    (1)预加载DOM树,代表是java官方的JDOM解析和社区的DOM4J解析,DOM4J更好用;
    (2)事件机制的SAX,代表就是SAXReader,该方法思路是一行一行的读取XML文件,没遇到一个节点,就看看有没有监听该事件的监听器,如果有就触发;
    Digester底层采用SAX(Simple API for XML)解析XML文件,所以对象转换由"事件"驱动,在遍历每个节点时,检查是否有匹配模式,如果有,则执行规则定义的操作,比如创建特定的Java对象,或调用特定对象的方法等。此处的XML元素根据匹配模式(matching pattern)识别,而相关操作由规则(rule)定义。

    2、tomcat使用digester解析server.xml代码流程

    Catalina.java::load()

    //用digester解析server.xml文件,把配置文件中的配置解析成java对象
    //1.准备好用来解析server.xml文件需要用的digester。
    Digester digester = createStartDigester();
    //2.server.xml文件作为一个输入流传入
    File file = configFile();
    InputStream inputStream = new FileInputStream(file);
    //3.使用inputStream构造一个sax的inputSource
    InputSource inputSource = new InputSource(file.toURI().toURL().toString());
    inputSource.setByteStream(inputStream);
    //4.把当前类压入到digester的栈顶,用来作为digester解析出来的对象的一种引用
    digester.push(this);
    //5.调用digester的parse()方法进行解析。
    digester.parse(inputSource);

    3、设置规则-createStartDigester()方法

    protected Digester createStartDigester() {
            long t1=System.currentTimeMillis();
            // 实例化一个Digester对象
            Digester digester = new Digester();
            // 设置为false表示解析xml时不需要进行DTD的规则校验  
            digester.setValidating(false);
            // 是否进行节点设置规则校验,如果xml中相应节点没有设置解析规则会在控制台显示提示信息  
            digester.setRulesValidation(true);
            // 将xml节点中的className作为假属性,不必调用默认的setter方法(一般的节点属性在解析时将会以属性值作为入参调用该节点相应对象的setter方法,而className属性的作用是提示解析器用该属性的值来实例化对象)
            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);
    
            // addObjectCreate方法的意思是碰到xml文件中的Server节点则创建一个StandardServer对象 
            digester.addObjectCreate("Server","org.apache.catalina.core.StandardServer","className");
            // 根据Server节点中的属性信息调用相应属性的setter方法,还会调用setPort、setShutdown方法,入参分别是8005、SHUTDOWN  
            digester.addSetProperties("Server");
            // 将Server节点对应的对象作为入参调用栈顶对象的setServer方法,这里的栈顶对象即下面的digester.push方法所设置的当前类的对象this,就是说调用MyDigester类的setMyServer方法
            digester.addSetNext("Server","setServer","org.apache.catalina.Server");
            
            // 碰到xml的Server节点下的Listener节点时取className属性的值作为实例化类实例化一个对象 
            //GlobalNamingResources 全局J2EE企业命名上下文
            digester.addObjectCreate("Server/GlobalNamingResources","org.apache.catalina.deploy.NamingResourcesImpl");
            digester.addSetProperties("Server/GlobalNamingResources");
            digester.addSetNext("Server/GlobalNamingResources","setGlobalNamingResources","org.apache.catalina.deploy.NamingResourcesImpl");
            //Server/Listener 按照className配置来创建  生命周期监听器
            //第二个参数 MUST be specified in the element
            digester.addObjectCreate("Server/Listener",null,"className");
            digester.addSetProperties("Server/Listener");
            digester.addSetNext("Server/Listener","addLifecycleListener","org.apache.catalina.LifecycleListener");
            //StandardService
            digester.addObjectCreate("Server/Service","org.apache.catalina.core.StandardService","className");
            digester.addSetProperties("Server/Service");
            digester.addSetNext("Server/Service","addService","org.apache.catalina.Service");
            //Service/Listener 按照className配置来创建
            digester.addObjectCreate("Server/Service/Listener",null, "className");
            digester.addSetProperties("Server/Service/Listener");
            digester.addSetNext("Server/Service/Listener","addLifecycleListener","org.apache.catalina.LifecycleListener");
            //StandardThreadExecutor
            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");
            //Connector
            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");
            //SSLHostConfig
            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");
            //Certificate
            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");
            //OpenSSLConf
            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");
            //OpenSSLConfCmd
            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");
            //Server/Service/Connector/Listener
            digester.addObjectCreate("Server/Service/Connector/Listener",null,"className");
            digester.addSetProperties("Server/Service/Connector/Listener");
            digester.addSetNext("Server/Service/Connector/Listener","addLifecycleListener","org.apache.catalina.LifecycleListener");
            //UpgradeProtocol 升级协议
            digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",null,"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;
        }

    4、解析动作-parse

    org.apache.tomcat.util.Digester::parse(inputSource);
    com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser::parse
    com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser::parse
    com.sun.org.apache.xerces.internal.parsers.XMLParser::parse
    com.sun.org.apache.xerces.internal.parsers.XIncludeAwareParserConfiguration::parse
    com.sun.org.apache.xerces.internal.parsers.XML11Configuration::parse
    com.sun.org.apache.xerces.internal.impl.XMLVersionDetector::startDocumentParsing  //设置属性编码
    org.apache.tomcat.util.Digester::startDocument
    com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl::scanDocument //解析整个文档
    com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl::next
    com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$ContentDriver::scanRootElementHook
    com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl::scanStartElement
    com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidator::startElement
    com.sun.org.apache.xerces.internal.parsersAbstractSAXParser::startElement
    org.apache.tomcat.util.Digester::startElement

    开始调用startElement对元素开始解析,先拼接模式然后获取其对应的规则,遍历所有规则,调用其对应规则实例的begin方法,这要求所有规则实现抽象类Rule,规则的添加在上文解析过程中。

    参考:
    Tomcat组件梳理—Digester的使用

  • 相关阅读:
    从头开始搭建分布式日志平台的docker环境
    spring mvc+ELK从头开始搭建日志平台
    两个与spring事务相关的问题
    shiro realm 注解失败问题解决过程
    如何解决CRUD操作中与业务无关的字段赋值
    通过angularjs的directive以及service来实现的列表页加载排序分页
    项目中应用eventbus解决的问题
    统一配置中心
    java枚举与.net中的枚举区别
    列表页的动态条件搜索
  • 原文地址:https://www.cnblogs.com/cac2020/p/12763164.html
Copyright © 2011-2022 走看看