load()方法按从上到下顺序分析(主要分析本人所没学过的知识点,其它略过。。。)。

Digester类作用

使用sax技术对xml进行解析

未开始解析时Digester.push(this)这个用来为catalina设置server

Digester的stack对象栈中持有Catalina对象,

解析xml过程中需要用到的类:

Rule:这个类有很多子类,为解析时遇到不同的匹配模式pattern调用不同的处理动作即不同rule。

  当解析到开始标记时会调用其子类ObjectCreateRule.begin()方法.

  如解析到<Server>时就会创建StandardServer类的实例并反射调用Digester的stack栈顶对象的setter方法(调用的方法通过传入的name值确定)。

  IntrospectionUtils.setProperty(top, name, value)top栈顶对象,name要设置的属性名---方法名的一部分,value要设置的属性值

  刚开始时栈顶元素是Catalina,即调用Catalina.setServer(Server object)方法设置Server为后面调用Server.start()做准备

  然后将StandardServer对象实例放入Digester的stack对象栈中,

  如上面所示会调用setter方法设置NamingResourcesImpl、NamingContextListener。调用addService()方法设置service

  当解析到<service>时StandardService实例化,并设置StandardService的Connector、Executor:StandardThreadExecutor、Container

上面Server,Service,Executor,Container,Connector对象设置都是在解析过程中设置了。

  1. A Digester processes an XML input stream by matching a series of element nesting
  2. patterns to execute Rules that have been added prior to the start of parsing. This
  3. package was inspired by the XmlMapper class that was part of Tomcat 3.0 and 3.1,
  4. but is organized somewhat differently.
    Digester通过匹配一系列元素嵌套模式来处理XML输入流,以执行在开始解析之前添加的规则。
    该软件包受到作为Tomcat 3.03.1的一部分的XmlMapper类的启发,但是它的组织方式略有不同。

具体的对象设置过程请跳转查看:

Catalina.createDigester方法详细理解

 一、CreateStartDigester()//Digester的初始化,为Xml的标签即解析模式增加处理规则rule
二、org.xml.sax.InputSource.
XML实体的单一输入源
创建一个带有系统标识符的新输入源。
应用程序也可以使用setPublicId来包含公共标识符,
或者setEncoding来指定字符编码,如果已知的话。
如果系统标识符是URL,则必须完全解析(它可能不是相对URL)。
inputSource = new InputSource(file.toURI().toURL().toString());//这个很标准file.toURL()已经不推荐使用了,要获取这个文件路径必须用file.toURI()进行转换
  1. /**如果还有一个指定的字符流,SAX解析器将忽略这一点,但它会使用一个字节流来优先打开一个URI连接本身。
  2. 如果应用程序知道字节流的字符编码,则应使用setEncoding方法进行设置。*/
  3. inputSource.setByteStream(inputStream);

 三、digester解析xml文件

digester.push(this);将catalina类放入对象栈顶
digester.parse(inputSource);这里开始解析xml数据资源

上面load方法中知道inputSource是指catalina.base/conf/server.xml文件字节流

public Object parse(InputSource input) {

configure();//提供这个Digester实例的懒惰配置的钩子。默认实现什么都不做,但子类可以根据需要重写。
getXMLReader().parse(input);
return (root);

}

返回用于解析输入文档的XMLReader。 FIX ME:在JAXP / XERCES中有一个bug,阻止使用包含DTD的模式的解析器。

Digester.getXMLReader() 
  1. public XMLReader getXMLReader() throws SAXException {
  2. if (reader == null) {
  3. reader = getParser().getXMLReader();
  4. }
  5.  
  6. reader.setDTDHandler(this);
  7. reader.setContentHandler(this);
  8.  
  9. if (entityResolver == null) {
  10. reader.setEntityResolver(this);
  11. } else {
  12. reader.setEntityResolver(entityResolver);
  13. }
  14.  
  15. reader.setProperty("http://xml.org/sax/properties/lexical-handler", this);
  16.  
  17. reader.setErrorHandler(this);
  18. return reader;
  19. }

getParser()

创建一个SAXParser

通过getFactory()获得SAXParserFactory工厂类

工厂类调用newSAXParser()创建parser实例

  1. public SAXParser getParser() {
  2.  
  3. // Return the parser we already created (if any)
  4. if (parser != null) {
  5. return (parser);
  6. }
  7.  
  8. // Create a new parser
  9. try {
  10. parser = getFactory().newSAXParser();
  11. } catch (Exception e) {
  12. log.error("Digester.getParser: ", e);
  13. return (null);
  14. }
  15.  
  16. return (parser);
  17.  
  18. }

得到XMLReader

SAXParser.getXMLReader() 返回由该类实现封装的org.xml.sax.XMLReader

getXMLReader方法中reader.setDTDHandler(this)注册DTD事件处理程序,将会监听SAXParser解析器报告的DTD事件

reader.setErrorHandler(this);注册错误事件处理程序

reader.setContentHandler(this); 

  1. 注册内容事件处理程序。如果应用程序没有注册内容处理程序,SAX解析器报告的所有内容事件将被默认忽略。
  2.  
  3. 应用程序可以在解析的中间注册一个新的或不同的处理程序,而SAX解析器必须立即开始使用新的处理程序
  4.  
  5. SAX编程中,需要为XMLReader设置相应的ContentHandler
    HandlerstartDocument,endDocument,startElement,endElementcharacters等方法将用于响应解析xml时的标签事件,
    可看到Digester继承于DefaultHandler类,而该类则实现ContentHandler接口,
    因此在对server.xml解析时将相应地调用到DigesterstartDocument,endDocument,startElement,endElementcharacters等方法

最后返回一个xmlreader执行

XMLReader.parse(InputSource input)
XMLReader将通过注册的事件处理程序提供有关XML文档的信息。
此方法是同步的:它将不会返回,直到解析结束。
如果客户端应用程序想要尽早终止解析,则应该抛出异常
当开始解析xml文档时,会向ContentHandler发送内容事件,即Digester。
  1. startDocument,endDocument,startElement,endElementcharacters等方法将会被调用
 
 
 
在调用所有解析方法之前会将调用

Digester.setDocumentLocator(Locator locator)设置Locator,报告文档相关事件。如错误事件
这个Locator包含的文档的编码,可以在startDocument中用到
  1. 提供定位器:如果这样做,它必须通过调用此方法在调用ContentHandler接口中的任何其他方
  2. 法之前将定位器提供给应用程序。定位器允许应用程序确定任何文档相关事件的结束位置,即使
  3. 解析器没有报告错误。通常,应用程序将使用此信息来报告其自身的错误(例如与应用程序业务
  4. 规则不匹配的字符内容)
1、Digester.startDocument() 

处理通知文档的开头,这里主要设置的编码The encoding used by the source XMl document.

((DocumentProperties.Encoding) root).setEncoding(((Locator2) locator).getEncoding());

再开始startElement()方法之前会先调用

startPrefixMapping(String prefix, String namespaceURI)//命名空间前缀进入范围的通知处理方法

这个方法主要是用HashMap<String, ArrayStack<String>>

namespaces 变量以prefix为key,ArrayStack为值保存数据ArrayStack保存着namespaceURI

  1. ArrayStack<String> stack = namespaces.get(prefix);
  2. if (stack == null) {
  3. stack = new ArrayStack<>();
  4. namespaces.put(prefix, stack);
  5. }
  6. stack.push(namespaceURI);

SAX XML reader将自动替换元素和属性名称的前缀。

然而,有些情况下,当应用程序需要在字符数据或属性值中使用前缀时,它们无法安全地自动扩展; start / endPrefixMapping事件将信息提供给应用程序,以在必要时在这些上下文中扩展前缀。

startPrefixMapping事件都将在相应的startElement事件之前立即发生,并且所有endPrefixMapping事件都将在相应的endElement事件之后立即发生

2、Digester.startElement(String namespaceURI, String localName, String qName, Attributes list)

  1. namespaceURI The Namespace URI, or the empty string if the element has no
  2.         Namespace URI or if Namespace processing is not being performed.
    命名空间URI,如果元素没有命名空间URI或未执行命名空间处理,则为空字符串
  1. localName
  2.     The local name (without prefix), or the empty string if Namespace processing is
  3.     not being performed.
    本地名称(无前缀),或为空字符串(如果命名空间处理) 没有执行。
    qName   The qualified name (with prefix), or the empty string
  4.      if qualified names are not available.
    限定名称(带前缀)或为空字符串 如果限定名称不可用。
    list   The attributes attached to the element. If
  5.       there are no attributes, it shall be an empty Attributes object.
    附加到元素的属性。如果没有属性,它将是一个空的Attributes对象。

到达XML元素的开始处理通知

 updateAttributes(list)更新属性中的系统值引用

格式为“$ {xxx}”的文本都将被系统属性中适当的值替换。

初始化标签体内容

bodyTexts用于周围元素的正文文本字符串缓冲区堆栈。The stack of body text string buffers for surrounding elements.

bodyText当前元素标签内的正文内容

获取路径名

将标签的路径字符赋给name。XML格式如:<Servers><Server><Server></Servers>,

当解析到<Server>标签时路径则为Servers/Server,在得到路径后又将该路径赋予match变量;

得到与标签体路径名匹配的Rule List,触发其begin方法

这部分内容是tomcat解析xml的重要部分

Rule.begin(String namespace, String name, Attributes attributes)

当遇到匹配的XML元素的开始时调用此方法。默认实现委托到不推荐使用的方法,而不使用命名空间和名称参数,以保留向后兼容性。

然后这个方法内调用Rule.begin(Attributes attributes) 开始匹配xml元素

3、Digester.characters(char[] buffer, int start, int length) 处理从XML元素的正文接收到的字符数据的通知。

buffer The characters from the XML document来自XML文档的字符

start Starting offset into the buffer开始偏移到缓冲区

length Number of characters from the buffer缓冲区中的字符数

  1. bodyText.append(buffer, start, length);接受元素标签内的正文内容

4、Digester.endElement(String namespaceURI, String localName, String qName)处理到达的XML元素的结束标记的通知

将bodyText中的变量值替换,如标签体中有${catalina.base},会使用System.getProperties()提取系统catalina.base的属性值

获取与标签路径名匹配的Rule,触发其body及end方法

matches再startElement中被复值了

List<Rule> rules = getRules().match(namespaceURI, match);

matches.push(rules);

  1. // Parse system properties
  2. bodyText = updateBodyText(bodyText);
  3. List<Rule> rules = matches.pop();
  4. rule.body(namespaceURI, name, bodyText);
  5. // Recover the body text from the surrounding element
  6.   bodyText = bodyTexts.pop();
  7.  
  8. rule.end(namespaceURI, name);
  9.  
  10. // Recover the previous match expression
  11. int slash = match.lastIndexOf('/');
  12. if (slash >= 0) {
  13. match = match.substring(0, slash);
  14. } else {
  15. match = "";
  16. }

执行完endElement()方法后元素标签解析完毕需要取消注册此前缀映射

Digester.endPrefixMapping(String prefix)就是处理的地方

  1. ArrayStack<String> stack = namespaces.get(prefix);
  2. stack.pop();
  3. if (stack.empty())
  4. namespaces.remove(prefix);

5、Digester.endDocument()

方法内容为:弹出所有还在栈中的对象,执行所有Rule的finish方法,执行clear方法清空相关堆栈(clear方法将Digester定义的变量设置为null,configure设置为false)


到这里xml解析完成。等有空再把上面缕缕

tomcat启动(三)Catalina分析-load方法分析的更多相关文章

  1. tomcat启动(三)Catalina简要分析

    上篇解析Bootstrap到 daemon.setAwait(true); daemon.load(args); daemon.start(); 这三个方法实际是反射调用org.apache.cata ...

  2. scala中ClassOf、asInstenceOf、isInstanceOf三个预定义方法分析

    classOf.isInstanceOf.asInstanceOf三个预定义方法分析 Scala的三个预定义(predefined)方法,我们经常用到:它们用来感觉很简单, 但是里面还是隐藏了一些细节 ...

  3. tomcat启动控制台中文乱码问题解决方法

    tomcat启动控制台中文乱码问题解决方法,修改tomcat安装路径/conf/logging.properties文件 java.util.logging.ConsoleHandler.encodi ...

  4. tomcat启动(四)Catalina分析-server的init()方法

    上一回load()方法解析讲到xml解析完成. load()内部接下来会获取server getServer().setCatalina(this); 这个server从createStartDige ...

  5. [Tomcat 源码分析系列] (二) : Tomcat 启动脚本-catalina.bat

    概述 Tomcat 的三个最重要的启动脚本: startup.bat catalina.bat setclasspath.bat 上一篇咱们分析了 startup.bat 脚本 这一篇咱们来分析 ca ...

  6. tomcat启动(五)Catalina分析-service.init

    上篇写到StandardService.init() 这个方法做什么呢?一起来看看. 这个类也是实现了Lifecycle 如图.这个图中i表示Interface接口.如Lifecycle,Contai ...

  7. 阿里云 centos7 tomcat 启动巨慢的解决方法(几分钟)

    方法一: 通过修改Tomcat启动文件-Djava.security.egd=file:/dev/urandom 通过修改JRE中的java.security文件securerandom.source ...

  8. tomcat启动(六)Catalina分析-StandardServer.start()

    从链接 Tomcat中组件的生命周期管理公共接口Lifecycle 可以知道调用的是StandardServer.startInternal() @Override protected void st ...

  9. Tomcat启动脚本catalina.sh

    1 - 概述脚本catalina.sh用于启动和关闭tomcat服务器,是最关键的脚本另外的脚本startup.sh和shutdown.sh都是使用不同的参数调用了该脚本该脚本的使用方法如下(引自该脚 ...

随机推荐

  1. C++ 11可变参数接口设计在模板编程中应用的一点点总结

    概述 本人对模板编程的应用并非很深,若要用一句话总结我个人对模板编程的理解,我想说的是:模板编程是对类定义的弱化. 如何理解“类定义的弱化”? 一个完整的类有如下几部分组成: 类的名称: 类的成员变量 ...

  2. NoSQL: Cassandra, HBase, RocksDB

    转自: http://www.linkedin.com/pulse/nosql-cassandra-hbase-rocksdb-siddharth-anand I've had the pleasur ...

  3. hdu 5037 周期优化

    http://acm.hdu.edu.cn/showproblem.php?pid=5037 有只青蛙踩石子过河,河宽m,有n个石子坐标已知.青蛙每次最多跳L.现在可以在河中再放一些石子,使得青蛙过河 ...

  4. 网友写的解决uniGUI限制的方法

    群友写的解决uniGUI试用版限制修改SessionTimeOut,思路很精巧,贴过来分享,感谢朋友的奉献.当然,如果真正用uniGUI实做项目,买份正版是正道! var   UniServerOpt ...

  5. WPF Auto LogOff

    Implementation of Auto Logoff Based on User Inactivity in WPF Application http://www.codeproject.com ...

  6. linux系统编程之信号(二):信号处理流程(产生、注册、注销、执行)

        对于一个完整的信号生命周期(从信号发送到相应的处理函数执行完毕)来说,可以分为三个阶段: 信号诞生 信号在进程中注册 信号在进程中的注销 信号处理函数执行 1    信号诞生     信号事件 ...

  7. 关于微信小程序登录,后端如何生成3rd_session?(后端为c#)

    各位大神,请教一个问题,现在是小程序端调用wx.login后,将code传入后端接口,后端发起微信服务器request获取openid和session_key,后端再自定义生成一个登录状态:3rd_s ...

  8. [ASP.NET]ASP.NET中常用的26个优化性能方法

    1. 数据库访问性能优化 数据库的连接和关闭 访问数据库资源需要创建连接.打开连接和关闭连接几个操作.这些过程需要多次与数据库交换信息以通过身份验证,比较耗费服务器资源.ASP.NET中提供了连接池( ...

  9. 在centos7升级jenkins

    找到jenkins的位置 使用下面的命令 ps -aux | grep jenkins enkins    5954  7.9 22.5 2695800 421088 ?      Ssl  20:5 ...

  10. CodeForces 540C Ice Cave (BFS)

    http://codeforces.com/problemset/problem/540/C       Ice Cave Time Limit:2000MS     Memory Limit:262 ...