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对象设置都是在解析过程中设置了。

A Digester processes an XML input stream by matching a series of element nesting
patterns to execute Rules that have been added prior to the start of parsing. This
package was inspired by the XmlMapper class that was part of Tomcat 3.0 and 3.1,
but is organized somewhat differently.
Digester通过匹配一系列元素嵌套模式来处理XML输入流,以执行在开始解析之前添加的规则。
该软件包受到作为Tomcat 3.0和3.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()进行转换
/**如果还有一个指定的字符流,SAX解析器将忽略这一点,但它会使用一个字节流来优先打开一个URI连接本身。
如果应用程序知道字节流的字符编码,则应使用setEncoding方法进行设置。*/
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() 
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()

创建一个SAXParser

通过getFactory()获得SAXParserFactory工厂类

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

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

得到XMLReader

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

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

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

reader.setContentHandler(this); 

注册内容事件处理程序。如果应用程序没有注册内容处理程序,SAX解析器报告的所有内容事件将被默认忽略。

应用程序可以在解析的中间注册一个新的或不同的处理程序,而SAX解析器必须立即开始使用新的处理程序

在SAX编程中,需要为XMLReader设置相应的ContentHandler,
该Handler的startDocument,endDocument,startElement,endElement及characters等方法将用于响应解析xml时的标签事件,
可看到Digester继承于DefaultHandler类,而该类则实现ContentHandler接口,
因此在对server.xml解析时将相应地调用到Digester的startDocument,endDocument,startElement,endElement及characters等方法

最后返回一个xmlreader执行

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

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

ArrayStack<String> stack = namespaces.get(prefix);
if (stack == null) {
stack = new ArrayStack<>();
namespaces.put(prefix, stack);
}
stack.push(namespaceURI);

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

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

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

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

namespaceURI The Namespace URI, or the empty string if the element has no
        Namespace URI or if Namespace processing is not being performed.
命名空间URI,如果元素没有命名空间URI或未执行命名空间处理,则为空字符串
localName
    The local name (without prefix), or the empty string if Namespace processing is
    not being performed.
本地名称(无前缀),或为空字符串(如果命名空间处理) 没有执行。
qName   The qualified name (with prefix), or the empty string
     if qualified names are not available.
限定名称(带前缀)或为空字符串 如果限定名称不可用。
list   The attributes attached to the element. If
      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缓冲区中的字符数

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);

// Parse system properties
bodyText = updateBodyText(bodyText);
List<Rule> rules = matches.pop();
rule.body(namespaceURI, name, bodyText);
// Recover the body text from the surrounding element
  bodyText = bodyTexts.pop(); rule.end(namespaceURI, name); // Recover the previous match expression
int slash = match.lastIndexOf('/');
if (slash >= 0) {
match = match.substring(0, slash);
} else {
match = "";
}

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

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

ArrayStack<String> stack = namespaces.get(prefix);
stack.pop();
if (stack.empty())
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. Android类装载器DexClassLoader的简单使用-----制作android插件的前奏

    声明:此篇文章借鉴<android内核剖析>整理得来. 一.装载器简介 “类装载器”(ClassLoader),顾名思义,就是用来动态装载class文件的.标准的Java SDK中有个Cl ...

  2. c++ 64位int

    转自:https://www.byvoid.com/blog/c-int64 C/C++的64位整型 在C/C++中,64为整型一直是一种没有确定规范的数据类型.现今主流的编译器中,对64为整型的支持 ...

  3. 洛谷P3066 [USACO12DEC]逃跑的Barn (线段树合并)

    题目描述It's milking time at Farmer John's farm, but the cows have all run away! Farmer John needs to ro ...

  4. Python学习-17.Python中的错误处理(二)

    错误是多种多样的,在 except 语句中,可以捕获指定的异常 修改代码如下: import io path = r'' mode = 'w' try: file = open(path,mode) ...

  5. 程序员MAC必备

    排名不分先后 • iTerm 2 终端工具(建议配合oh-my-zsh使用) • Shadowsocks     ***工具 (可用于FQ) • Foxmail 邮箱工具 (适用于企业邮箱登陆) • ...

  6. 我所理解的网络游戏<一>:网游的顶层设计

    网游的基本结构 各大模块的基本功能如下 · 服务器端 登陆服:处理新建玩家.登陆逻辑. 场景服:处理场景服中的逻辑. 中心服:处理跨服的逻辑,实现不同场景服进程的数据调度,以及向数据库查询数据. 数据 ...

  7. sqlServer存储过程与sql语句的区别

    sqlServer   存储过程与sql语句的区别 sql存储过程与sql语句的区别: 从以下几个方面考虑: 1.编写: 存储过程:编写比较难: sql语句:相对简单: 2.性能: 存储过程:高,可移 ...

  8. 使用原生方法从kafka消费消息

    kafka最早是linkedin开发的一套高性能类队列结构,具有发布—订阅功能.现在是apache的项目之一.支持很多种客户端从其中进行consume,网上也有许多第三方的客户端(注1),但下面我们只 ...

  9. DateTimeField如何自动设置为当前时间并且能被修改 ——django日期时间字段的使用

    参考于:https://www.cnblogs.com/huchong/p/7895263.html 创建django的model时,有DateTimeField.DateField和TimeFiel ...

  10. javascript中的数据类型和变量

    Number JavaScript不区分整数和浮点数,统一用Number表示,以下都是合法的Number类型: 123; // 整数123 0.456; // 浮点数0.456 1.2345e3; / ...