前一篇文章里最后看到Bootstrap的main方法最后会调用org.apache.catalina.startup.Catalina对象的load和start两个方法,那么就来看看这两个方法里面到底做了些什么。

load方法:

Java代码  

/**

* Start a new server instance.

*/

public void load() {

long t1 = System.nanoTime();

initDirs();

// Before digester - it may be needed

initNaming();

// Create and execute our Digester

Digester digester = createStartDigester();

InputSource inputSource = null;

InputStream inputStream = null;

File file = null;

try {

file = configFile();

inputStream = new FileInputStream(file);

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

digester.push(this);

digester.parse(inputSource);

} catch (SAXParseException spe) {

log.warn("Catalina.start using " + getConfigFile() + ": " +

spe.getMessage());

return;

} catch (Exception e) {

log.warn("Catalina.start using " + getConfigFile() + ": " , e);

return;

} finally {

try {

inputStream.close();

} catch (IOException e) {

// Ignore

}

}

getServer().setCatalina(this);

// Stream redirection

initStreams();

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

}

}

long t2 = System.nanoTime();

if(log.isInfoEnabled()) {

log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");

}

}

这个110行的代码看起来东西挺多,把注释、异常抛出、记录日志、流关闭、非空判断这些放在一边就会发现实际上真正做事的就这么几行代码:

Java代码  

Digester digester = createStartDigester();

inputSource.setByteStream(inputStream);

digester.push(this);

digester.parse(inputSource);

getServer().setCatalina(this);

getServer().init();

做的事情就两个,一是创建一个Digester对象,将当前对象压入Digester里的对象栈顶,根据inputSource里设置的文件xml路径及所创建的Digester对象所包含的解析规则生成相应对象,并调用相应方法将对象之间关联起来。二是调用Server接口对象的init方法。

这里碰到了Digester,有必要介绍一下Digester的一些基础知识。一般来说Java里解析xml文件有两种方式:一种是Dom4J之类将文件全部读取到内存中,在内存里构造一棵Dom树的方式来解析。一种是SAX的读取文件流,在流中碰到相应的xml节点触发相应的节点事件回调相应方法,基于事件的解析方式,优点是不需要先将文件全部读取到内存中。

Digester本身是采用SAX的解析方式,在其上提供了一层包装,对于使用者更简便友好罢了。最早是在struts1里面用的,后来独立出来成为apache的Commons下面的一个单独的子项目。Tomcat里又把它又封装了一层,为了描述方便,直接拿Tomcat里的Digester建一个单独的Digester的例子来介绍。

Java代码  

package org.study.digester;

import java.io.IOException;

import java.io.InputStream;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import junit.framework.Assert;

import org.apache.tomcat.util.digester.Digester;

import org.xml.sax.InputSource;

public class MyDigester {

private MyServer myServer;

public MyServer getMyServer() {

return myServer;

}

public void setMyServer(MyServer myServer) {

this.myServer = myServer;

}

private Digester createStartDigester() {

// 实例化一个Digester对象

Digester digester = new Digester();

// 设置为false表示解析xml时不需要进行DTD的规则校验

digester.setValidating(false);

// 是否进行节点设置规则校验,如果xml中相应节点没有设置解析规则会在控制台显示提示信息

digester.setRulesValidation(true);

// 将xml节点中的className作为假属性,不必调用默认的setter方法(一般的节点属性在解析时将会以属性值作为入参调用该节点相应对象的setter方法,而className属性的作用是提示解析器用该属性的值来实例化对象)

HashMap, List> fakeAttributes = new HashMap, List>();

ArrayList attrs = new ArrayList();

attrs.add("className");

fakeAttributes.put(Object.class, attrs);

digester.setFakeAttributes(fakeAttributes);

// addObjectCreate方法的意思是碰到xml文件中的Server节点则创建一个MyStandardServer对象

digester.addObjectCreate("Server",

"org.study.digester.MyStandardServer", "className");

// 根据Server节点中的属性信息调用相应属性的setter方法,以上面的xml文件为例则会调用setPort、setShutdown方法,入参分别是8005、SHUTDOWN

digester.addSetProperties("Server");

// 将Server节点对应的对象作为入参调用栈顶对象的setMyServer方法,这里的栈顶对象即下面的digester.push方法所设置的当前类的对象this,就是说调用MyDigester类的setMyServer方法

digester.addSetNext("Server", "setMyServer",

"org.study.digester.MyServer");

// 碰到xml的Server节点下的Listener节点时取className属性的值作为实例化类实例化一个对象

digester.addObjectCreate("Server/Listener", null, "className");

digester.addSetProperties("Server/Listener");

digester.addSetNext("Server/Listener", "addLifecycleListener",

"org.apache.catalina.LifecycleListener");

digester.addObjectCreate("Server/Service",

"org.study.digester.MyStandardService", "className");

digester.addSetProperties("Server/Service");

digester.addSetNext("Server/Service", "addMyService",

"org.study.digester.MyService");

digester.addObjectCreate("Server/Service/Listener", null, "className");

digester.addSetProperties("Server/Service/Listener");

digester.addSetNext("Server/Service/Listener", "addLifecycleListener",

"org.apache.catalina.LifecycleListener");

return digester;

}

public MyDigester() {

Digester digester = createStartDigester();

InputSource inputSource = null;

InputStream inputStream = null;

try {

String configFile = "myServer.xml";

inputStream = getClass().getClassLoader().getResourceAsStream(

configFile);

inputSource = new InputSource(getClass().getClassLoader()

.getResource(configFile).toString());

inputSource.setByteStream(inputStream);

digester.push(this);

digester.parse(inputSource);

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

inputStream.close();

} catch (IOException e) {

// Ignore

}

}

getMyServer().setMyDigester(this);

}

public static void main(String[] agrs) {

MyDigester md = new MyDigester();

Assert.assertNotNull(md.getMyServer());

}

}

上面是我自己写的一个拿Tomcat里的Digester的工具类解析xml文件的例子,关键方法的调用含义已经在注释中写明,解析的是项目源文件发布的根目录下的myServer.xml文件。

Xml代码  

className="org.apache.catalina.core.JasperListener" />

className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

Digester的使用一般来说有4步:1.实例化一个Digester对象,并在对象里设置相应的节点解析规则。2.设置要解析的文件作为输入源(InputSource),这里InputSource与SAX里的一样,用以表示一个xml实体。3.将当前对象压入栈,4.调用Digester的parse方法解析xml,生成相应的对象。第3步中将当前对象压入栈中的作用是可以保存一个到Digester生成的一系列对象直接的引用,方便后续使用而已,所以不必是当前对象,只要有一个地方存放这个引用即可。

这里有必要说明的是很多文章里按照代码顺序来描述Digester的所谓栈模型来讲述addSetNext方法时的调用对象,实际上我的理解addSetNext方法具体哪个调用对象与XML文件里的节点树形结构相关,当前节点的父节点是哪个对象该对象就是调用对象。可以试验一下把这里的代码顺序打乱仍然可以按规则解析出来,createStartDigester方法只是在定义解析规则,具体解析与addObjectCreate、addSetProperties、addSetNext这些方法的调用顺序无关。Digester自己内部在解析xml的节点元素时增加了一个rule的概念,addObjectCreate、addSetProperties、addSetNext这些方法内部实际上是在添加rule,在最后解析xml时将会根据读取到的节点匹配相应节点路径下的rule,调用内部的方法。关于Tomcat内的Digester的解析原理以后可以单独写篇文章分析一下。

该示例的代码在附件中,将其放入tomcat7的源代码环境中即可直接运行。

看懂了上面的例子Catalina的createStartDigester方法应该就可以看懂了,它只是比示例要处理的节点类型更多,并且增加几个自定义的解析规则,如384行在碰到Server/GlobalNamingResources/节点时将会调用org.apache.catalina.startup.NamingRuleSet类中的addRuleInstances方法添加解析规则。

要解析的XML文件默认会先找conf/server.xml,如果当前项目找不到则通过其他路径找xml文件来解析,这里以默认情况为例将会解析server.xml

Xml代码  

type="org.apache.catalina.UserDatabase"

description="User database that can be updated and saved"

factory="org.apache.catalina.users.MemoryUserDatabaseFactory"

pathname="conf/tomcat-users.xml" />

connectionTimeout="20000"

redirectPort="8443" />

resourceName="UserDatabase"/>

unpackWARs="true" autoDeploy="true">

prefix="localhost_access_log." suffix=".txt"

pattern="%h %l %u %t "%r" %s %b" />

这样经过对xml文件的解析将会产生org.apache.catalina.core.StandardServer、org.apache.catalina.core.StandardService、org.apache.catalina.connector.Connector、org.apache.catalina.core.StandardEngine、org.apache.catalina.core.StandardHost、org.apache.catalina.core.StandardContext等等一系列对象,这些对象从前到后前一个包含后一个对象的引用(一对一或一对多的关系)。

解析完xml之后关闭文件流,接着设置StandardServer对象(该对象在上面解析xml时)的catalina的引用为当前对象,这种对象间的双向引用在Tomcat的很多地方都会碰到。

接下来将调用StandardServer对象的init方法。

上面分析的是Catalina的load方法,上一篇文章里看到Bootstrap类启动时还会调用Catalina对象的start方法,代码如下:

Java代码  

/**

* Start a new server instance.

*/

public void start() {

if (getServer() == null) {

load();

}

if (getServer() == null) {

log.fatal("Cannot start server. Server instance is not configured.");

return;

}

long t1 = System.nanoTime();

// Start the new server

try {

getServer().start();

} catch (LifecycleException e) {

log.fatal(sm.getString("catalina.serverStartFail"), e);

try {

getServer().destroy();

} catch (LifecycleException e1) {

log.debug("destroy() failed for failed Server ", e1);

}

return;

}

long t2 = System.nanoTime();

if(log.isInfoEnabled()) {

log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");

}

// Register shutdown hook

if (useShutdownHook) {

if (shutdownHook == null) {

shutdownHook = new CatalinaShutdownHook();

}

Runtime.getRuntime().addShutdownHook(shutdownHook);

// If JULI is being used, disable JULI's shutdown hook since

// shutdown hooks run in parallel and log messages may be lost

// if JULI's hook completes before the CatalinaShutdownHook()

LogManager logManager = LogManager.getLogManager();

if (logManager instanceof ClassLoaderLogManager) {

((ClassLoaderLogManager) logManager).setUseShutdownHook(

false);

}

}

if (await) {

await();

stop();

}

}

这里最主要的是调用StandardServer对象的start方法。

经过以上分析发现,在解析xml产生相应一系列对象后会顺序调用StandardServer对象的init、start方法,这里将会涉及到Tomcat的容器生命周期(Lifecycle),关于这点留到下一篇文章中分析。

java digester map_Tomcat7启动分析(三)Digester的使用(转载)相关推荐

  1. linux内核启动分析 三,Linux内核分析 实验三:跟踪分析Linux内核的启动过程

    贺邦 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 一. 实验过程 ...

  2. JBOSS java.lang.NoClassDefFoundError: org/apache/commons/digester/RuleSet

    经常在启动JBOSS的时候,发现在myeclipse的console中报错java.lang.NoClassDefFoundError: org/apache/commons/digester/Rul ...

  3. 深入java并发包源码(三)AQS独占方法源码分析

    深入java并发包源码(一)简介 深入java并发包源码(二)AQS的介绍与使用 深入java并发包源码(三)AQS独占方法源码分析 AQS 的实现原理 学完用 AQS 自定义一个锁以后,我们可以来看 ...

  4. Java程序初始化启动自动执行的三种方法

    目录 @PostConstruct注解 CommandLineRunner接口 ApplicationRunner 接口 @Order注解设置启动顺序 分享一下自己用过的java程序初始化启动自动执行 ...

  5. 内核启动分析(三)——zImage 解压缩阶段

          在上阶段,主要是U-BOOT 向内核传递一些参数.而这些参数是通过 struct tag来传递的.U-boot 把要传递给 kernel 的东西保存在 struct tag 数据结构中,启 ...

  6. Java线程池ThreadPoolExecutor使用和分析(三) - 终止线程池原理

    相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...

  7. java中如何启动一个新的线程三种方法

    java开启新线程的三种方法: 方法1:继承Thread类 1):定义bai一个继承自Java.lang.Thread类的du类A. 2):覆盖zhiA类Thread类中的run方法. 3):我们编写 ...

  8. OK6410开发板Uboot学习总结----(三)从SD卡启动分析

    前面讲了Uboot启动流程和如何修改调试串口,相信大家对Uboot已经有了初步的了解,今天来进行更深一点的分析.上篇文章 OK6410开发板Uboot学习总结----(二)修改调试打印串口 遗留一个问 ...

  9. java超线程_超线程多核心下Java多线程编程技术分析

    在学习编程的过程中,我觉得不止要获得课本的知识,更多的是通过学习技术知识提高解决问题的能力,这样我们才能走在最前方,本文主要讲述超线程多核心下Java多线程编程技术分析,更多Java专业知识,广州疯狂 ...

  10. 【Android 性能优化】应用启动优化 ( 安卓应用启动分析 | Launcher 应用启用普通安卓应用 | 应用进程分析 )

    文章目录 一. Launcher 应用 startActivitySafely 方法分析 二. Launcher 中的 startActivity(View v, Intent intent, Obj ...

最新文章

  1. 数据挖掘公司D square nv 完成500万欧元B轮融资
  2. 计数排序vs基数排序vs桶排序
  3. php 附近的距离,PHP查询附近的人及其距离的实现方法_PHP
  4. Linux系统编程:pipe匿名管道的使用,实现linux命令下管道命令
  5. python课程索引-0222
  6. 4月编程语言排行榜:C++ 重回前三,PHP 呈下降势头
  7. 笔记本电脑处理器_华硕推出首款第11代英特尔处理器VivoBook,ZenBook笔记本电脑...
  8. Intel与三星合作新移动操作系统Tizen 拥抱HTML5
  9. solidworks流体模拟分析概述
  10. idrac给服务器重装系统,DELL服务器如何使用iDRAC安装操作系统
  11. SQL Server 日期 字符串 格式转换 函数 datetime convert
  12. 2.15 随机存取存储器与只读存储器
  13. 2019-11-29-win10-UWP-Controls-by-function
  14. 北洋大时代:大师们的理想国札记-随想篇
  15. web移动开发总结(四)
  16. 微信小程序【网易云音乐实战】(第四篇 用户登录、本地存储、视频播放、上拉下拉刷新)
  17. Python 猜数字小游戏,3次机会
  18. 欢迎同学们参加新乡学院2019年3D打印暑期夏令营
  19. 统一短沟道圆柱形围栅MOSFET紧凑模型
  20. 第十六次 Java作业:使用阿里云提供的API查询天气预报

热门文章

  1. hua图软件 mac_实用电脑绘图软件~推荐_mac_微软怎么样_智能_魅可怎么样_圣诞节去哪玩_ipad_绘图软件_科技数码_应用推荐...
  2. android 限制后台进程,不超过4个进程 开发者选项,后台允许不超
  3. 基于SSH的在线问卷调查系统的设计与实现
  4. python xlwt_python xlwt模块简介
  5. 寻星时卫星数字电视接收机的信号检测功能
  6. AnkhSvn安装及使用
  7. 容差分析相关的计算公式
  8. 高等数学张宇18讲 第一讲 高等数学常用基础知识
  9. 【Matlab土壤分类】多类SVM土壤分类【含GUI源码 1398期】
  10. QComboBox下拉框样式