Tomcat8源码笔记(三)Catalina加载过程
之前介绍过 Catalina加载过程是Bootstrap的load调用的 Tomcat8源码笔记(二)Bootstrap启动
按照Catalina的load过程,大致如下: 接下来一步步分析加载过程
一.initDirs
从系统环境变量、VM参数中读取java.io.tmpdir, 并校验文件夹合法性; 未指定java.io.tmpdir,会抛出异常,所以我们启动时指定VM参数:
-Djava.io.tmpdir=E:/Tomcat_Source_Code/apache-tomcat-8.0.53-src/catalina-home/temp
二.initNaming
initNaming设置一些额外的环境变量,在创建Digester时可能会用到.
三.createStartDigester
createStartDigester作用: 实例化Digester,设置validating标志位false,rulesValidating标志位true,fakeAttributes为{Object.class=[className]},useContextClassLoader标志位true,以上几种属性标志位是为了进行解析server.xml而设置的 。 再接着添加了一大段的 addObjectCreate 、addSetProperties、addSetNext, 这些是用来设置解析server.xml的规则,Tomcat的server.xml我们没见过xsd、DTD来约束文档书写规则吧,但是也不能不按照顺序随意些,比如我们不能将Servers标签写到Service标签里面吧。下面会具体分析添加规则部分。
贴上一份Tomcat8中默认的Server.xml内容,虽然平时部署到Tomcat也没怎么仔细关注过它,可能关注也只是关注到Tomcat端口而已、或者简单配置虚拟图片服务器。现在仔细看这份xml文档,与常见的spring、mybatis的文档不同,前面没有声明DTD或XSD,所以Tomcat要自己定义规则、自己按照规则来解析这样一份XML。
四.Rule抽象类
Rule抽象类的定义如下,省略了setter/getter方法,可以看出来Rule两个属性:关联的Digester以及命名空间namespaceURI,此外的方法就是begin、body、end、finish,这就是一个Rule规则实例化的生命周期,就是按顺序解析XML,按顺序调用begin、body、end、finish方法就能实例化对象;
Rule具体的实现类下面分析,代码中Digester又是addObjectCreate 、addSetProperties、addSetNext一大串,先来addObjectCreate:
添加ObjectCreate时候,会先将Rule和Digester关联,然后每个规则加入到Digester的rules属性中,rules属性为Rules接口,默认采用RuleBase作为实现类。
Rules和RuleBase
RuleBase的add(String,Rule)方法:
rules属性Rule的集合,目的是有序地保存最开始注册的Rule规则;cache属性,按照pattern来保存,目的是比如Server组件,创建ObjectCreate、属性赋值setPropertiesRule、设置调用方法SetNextRule三个属性规则,通过pattern 字符串就能将同一组件的不同规则保存在一起. 而pattern中 / 用来分隔标签下的子标签,比如Server下的Service,Server/Service.
五.configFile
configFile默认为conf/server.xml,Bootstrap的catalinaBase之前Bootstrao启动时候静态代码块赋的值,从系统变量或VM参数中读取catalina.base;简而言之,就是读取catalina.base/conf/server.xml文件.
六.解析server.xml
inputStream就是上面server.xml的FileInputStream ,赋给InputSource实例. push以及parse方法下面记录.
六.一 Digester的push方法
Digester实例将Catalina实例存入stack中,并且Digester的root属性也为Catalina实例,这里的ArrayStack是Tomcat自己实现的类,继承自ArrayList。 这一步将Catalina放入Digester的stack集合相当于第一位置,下标为0的位置,很关键!
六.二 Digester的parse方法, 解析server.xml的地方
configure是初始化打印日志log ; getXmlReader深入发现就是调用SAXParserFactory.newInstance().newSAXParser(),采用SAX的方式解析server.xml,并且设置内容解析器为this对象,也就是Digester,Digester重写了解析的规则。
很想贴一张SAXParser解析流程图,找了半天没搜到,我简单介绍下,SAX解析过程中DefaultHandler类,Digester就继承了DefaultHandler2,当然也继承了DefaultHandler。这个接口呢,startDocument开始解析xml时执行,startElement开始解析元素时执行,characters解析元素内容,endElement结束元素解析,然后循环往复解析,直到解析完成之后endDocument . SAX解析xml优点内存占用小,解析速度快,因为一边读取xml一边解析。Digester类的startDocument就没必要看了,直接从startElement开始记录.
startElement解析过程
server.xml中元素namespaceURI都为空,match解析到Server就是Server,解析到Server下Service就是Server/Service,与之间加入到Digester的pattern是一致的,这里就按照这个规则找到Rule的集合,加入到matches中,并且遍历Rule集合分别调用begin方法,比如以Server元素为例。
Server标签解析:
Server一共有三个Rule,分别是ObjectCreateRule、SetPropertiesRule、SetNextRule
此时ObjectCreateRule的className为org.apache.catalina.core.StandardServer,attributeName为className。 尝试从xml解析的attributes中找className,没有的情况下就用前面的className,使用当前线程的类加载器加载StandardServer,并且使用空参构造器实例化一个StandardServer,具体的StandardServer实例化下章记录,实例化完成的server加入到digester的stack,就是前面说的那个ArrayList子类中,第一位是Catalina,所以StandardServer是第二位.
SetPropertiesRule
digester peek方法返回栈首,深入查看发现就是其stack中最后一位元素,即上面的StandardServer;之后遍历attributes,可以理解为键值对集合,如果键是”className“,isFakeAttribute返回true,后面设置属性的方法就不会执行;如果键不是“className“,就会给StandardServer设置属性,IntrospectionUtils.setProperty可以理解为给对象属性赋值的方法。 比如常见的 <Server port="8005" shutdown="SHUTDOWN"> ,这个xml解析之后attributes就是{port=8005,shutdown=SHUTDOWN},给对象属性赋值方法,也就是遍历对象的方法尝试寻找setPort/setShutdown,然后反射调用。
SetNextRule的begin方法没有做任何处理,跳过;另外server.xml中元素都没有body内容,characters我们也跳过;下面SAX解析发现又会跳进入到startElement中,因为Server元素没有结束,还有子元素要解析。
下面继续以一个Listener元素进行分析,仍然是startElement,之前match=”Server”,还没结束,现在match变成了“Server/Listener”
StringBuilder sb = new StringBuilder(match);
if (match.length() > 0) {
sb.append('/');
}
sb.append(name);
match = sb.toString();
if (debug) {
log.debug(" New match='" + match + "'");
} // Fire "begin" events for all relevant rules
List<Rule> rules = getRules().match(namespaceURI, match);
matches.push(rules);
之前向Digester添加解析规则时,添加了这样的规则:
所以现在通过Server/Listener同样可以得到三个Rule,和Server一样解析,调用空参构造器初始化第一个VersionLoggerListener,设置属性(虽然没有属性值要设置,因为className不在设置属性范围内),setNextRule(虽然这个begin什么也没做), 可以进入endElement分析,第一个endElement是Listener元素的:
endElement伪代码如下: matches也是ArrayStack类型的,pop返回ArrayList集合的最后一个元素,也就是Listener的List<Rule>,遍历Rule集合,分别调用其body方法;因为Tomcat的server.xml大部分或者几乎就没有元素体有内容的元素,所以Rule实现类的body方法都是空实现; 逆序遍历Rule集合,分别调用Rule的end方法 ; 最后结束一个元素,就将match的 / 去掉一重,保证下次解析正确性;
SetNextRule的end方法记录:
peek(0)返回digester中最后添加的一个元素,peek(1)返回倒数第二个添加的元素,分别是VersionLoggerListener以及StandardServer,反射调用peek(1)也就是StandardServer的addLifecycleListener,将peek(0)添加到StandardServer的监听器集合。
ObjectCreateRule的end方法
将当前正在创建的元素从digester中移除,ObjectCreateRule所以是最后执行的,start的时候顺序调用Rule集合,end时候逆序调用Rule集合;
上述就是Server元素所有子元素的解析流程,循环往复,直到最后才解析Server元素的endDocument方法,而前面分析得到Rule集合的end方法,只需要关注SetNextRule和ObjectCreateRule的end方法即可。到了Server时候Digester的stack中元素如下,[Catalina,StandardServer],所以按照分析会调用Catalina的setServer将解析完成的StandardServer赋给Catalina ; 而StandardServer的ObjectCreateRule会将 StandardServer从Digester移除; 加载过程中另外解析的xml,因为暂时不知道用途,这里就不记录了。
七. Catalina加载过程后续步骤
之前将StandardServer赋给了Catalina,现在也将Catalina赋给StandardServer,双向引用;之后给StandardServer设置catalina.home和catalina.base;初始化流,主要是控制台输出流改变,再之后调用StandardServer的init方法初始化容器!StandardServer初始化工作比较复杂,留作下篇博客记录。
八
简单按照Tomcat的server.xml解析规则,自定义了一个测试类,完成解析XML,目的是查看解析的效果:对Tomcat各个组件有个直观的了解。
import org.apache.catalina.*;
import org.apache.catalina.Server;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.deploy.NamingResourcesImpl;
import org.apache.catalina.startup.*;
import org.apache.tomcat.util.digester.ArrayStack;
import org.apache.tomcat.util.digester.Digester;
import org.apache.tomcat.util.digester.Rule;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.ext.DefaultHandler2;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List; public class ParserTest {
private String match="";
private ArrayStack<List<Rule>> stack=new ArrayStack(); public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
ParserTest test = new ParserTest();
test.test();
} public void test() throws IOException, ParserConfigurationException, SAXException {
SAXParserFactory factory = SAXParserFactory.newInstance();
InputSource inputSource = new InputSource();
inputSource.setByteStream(new FileInputStream("E:\\Tomcat_Source_Code\\apache-tomcat-8.0.53-src\\conf\\server.xml"));
Digester digester=new Digester();
addRule(digester);
digester.push(new Catalina());
factory.newSAXParser().parse(inputSource,new DefaultHandler2(){
@Override
public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException {
String name = localName;
if ((name == null) || (name.length() < 1)) {
name = qName;
}
if (match.length()>0) match+="/";
match+=name;
List<Rule> rules = digester.getRules().match(uri, ParserTest.this.match);
stack.push(rules);
if(rules!=null && rules.size()>0){
for(int i=0;i<rules.size();i++){
Rule rule = rules.get(i);
try {
rule.begin(uri,name,attributes);
} catch (Exception e) {
e.printStackTrace();
}
}
}
} @Override
public void endElement(final String uri, final String localName, final String qName) throws SAXException {
String name = localName;
if ((name == null) || (name.length() < 1)) {
name = qName;
}
List<Rule> rules = stack.pop();
if(rules!=null && rules.size()>0){
for(int i=0;i<rules.size();i++){
Rule rule = rules.get(i);
try {
rule.body(uri,name,"");
} catch (Exception e) {
e.printStackTrace();
}
}
} if(rules!=null){
for(int j=rules.size()-1;j>=0;j--){
Rule rule = rules.get(j);
try {
rule.end(uri,name);
} catch (Exception e) {
e.printStackTrace();
}
}
}
if(match.lastIndexOf("/")>0){
match=match.substring(0,match.lastIndexOf("/"));
}else{
match="";
}
} @Override
public void characters(final char[] ch, final int start, final int length) throws SAXException {
super.characters(ch, start, length);
} @Override
public void endDocument() throws SAXException {
if(stack.size()>0){
stack.pop();
}
Iterator<Rule> iterator = digester.getRules().rules().iterator();
while(iterator.hasNext()){
Rule rule = iterator.next();
try {
rule.finish();
} catch (Exception e) {
e.printStackTrace();
}
}
match="";
stack.clear();
}
});
System.out.println("解析XML完毕:");
Catalina catalina = (Catalina) digester.getRoot();
System.out.println("Cataline信息:"+catalina);
Server server = catalina.getServer();
System.out.println("Server信息:"+server);
System.out.printf("Server端口:%s,SHUTDOWN:%s\r\n",server.getPort(),server.getShutdown());
NamingResourcesImpl globalNamingResources = server.getGlobalNamingResources();
System.out.println("GlobalNamingResources信息:"+globalNamingResources);
LifecycleListener[] listeners = server.findLifecycleListeners();
for (LifecycleListener listener:listeners) {
System.out.println("Listener信息:"+listener);
}
Service[] services = server.findServices();
for (Service service:services) {
System.out.println("Service信息:"+service.getName());
Connector[] connectors = service.findConnectors();
for(Connector connector:connectors){
System.out.println("Connector信息:"+connector.getProtocol());
}
Container container = service.getContainer();
System.out.println("Container容器信息:"+container.getName());
Container[] children = container.findChildren();
int i=1;
for(Container child:children){
System.out.println(container.getName()+"子容器"+(i++)+"信息:"+child.getName());
if(child instanceof StandardHost){
StandardHost host= (StandardHost) child;
System.out.printf("APP_BASE: %s, UnpackWARS: %s, Auto_Deploy:%s\r\n" ,host.getAppBase(),host.isUnpackWARs(),host.getAutoDeploy());
Valve[] valves = host.getPipeline().getValves();
for(Valve valve:valves){
System.out.println(valve.getClass());
}
}
}
}
} public static void addRule(Digester digester){
HashMap<Class<?>, List<String>> fakeAttributes = new HashMap<>();
ArrayList<String> attrs = new ArrayList<>();
attrs.add("className");
fakeAttributes.put(Object.class, attrs);
digester.setFakeAttributes(fakeAttributes);
digester.setUseContextClassLoader(true); // Configure the actions we will be using
digester.addObjectCreate("Server","org.apache.catalina.core.StandardServer","className"); //初始化的Server对象StandardServer
digester.addSetProperties("Server");
digester.addSetNext("Server", "setServer", "org.apache.catalina.Server"); digester.addObjectCreate("Server/GlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources",
"setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl"); digester.addObjectCreate("Server/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Listener");
digester.addSetNext("Server/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener"); digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service"); digester.addObjectCreate("Server/Service/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Listener");
digester.addSetNext("Server/Service/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener"); //Executor
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"); digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
digester.addRule("Server/Service/Connector",
new SetAllPropertiesRule(new String[]{"executor"}));
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector");
digester.addObjectCreate("Server/Service/Connector/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Connector/Listener");
digester.addSetNext("Server/Service/Connector/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener"); // 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/"));
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/")); // When the 'engine' is found, set the parentClassLoader.
digester.addRule("Server/Service/Engine",new SetParentClassLoaderRule(Thread.currentThread().getContextClassLoader()));
} public static class SetParentClassLoaderRule extends Rule { public SetParentClassLoaderRule(ClassLoader parentClassLoader) { this.parentClassLoader = parentClassLoader; } ClassLoader parentClassLoader = null; @Override
public void begin(String namespace, String name, Attributes attributes)
throws Exception { if (digester.getLogger().isDebugEnabled()) {
digester.getLogger().debug("Setting parent class loader");
} Container top = (Container) digester.peek();
top.setParentClassLoader(parentClassLoader); }
}
}
查看解析效果: 也让我们大致对Tomca组件有个了解,Catalina持有Server,一个Server持有多个Service,Service组件持有多个Connector连接器,负责对外监听HTTP、AJP等等连接;Service组件只能有一个Container,通常是Engine,Engine容器中有Host容器,Host中就是Context容器;
九. 总结
Catalina加载的过程解析catalina.home下conf/server.xml,并完成实例化工作,并且调用了StandardServer init方法,总体流程是这样,具体细节后面再记录。
Tomcat8源码笔记(三)Catalina加载过程的更多相关文章
- Dubbo源码分析之ExtensionLoader加载过程解析
ExtensionLoader加载机制阅读: Dubbo的类加载机制是模仿jdk的spi加载机制: Jdk的SPI扩展加载机制:约定是当服务的提供者每增加一个接口的实现类时,需要在jar包的META ...
- Tomcat8源码笔记(六)连接器Connector分析
根据 Tomcat8源码笔记(五)组件Container分析 前文分析,StandardService的初始化重心由 StandardEngine转移到了Connector的初始化,本篇记录下Conn ...
- Tomcat8源码笔记(五)组件Container分析
Tomcat8源码笔记(四)Server和Service初始化 介绍过Tomcat中Service的初始化 最先初始化就是Container,而Container初始化过程是咋样的? 说到Contai ...
- Tomcat8源码笔记(四)Server和Service初始化
上一章 简单说明下Tomcat各个组件: Server:服务器,Tomcat服务器,一个Tomcat只有一个Server组件; Service:业务层,是Server下最大的子容器,一个Server可 ...
- Tomcat8源码笔记(八)明白Tomcat怎么部署webapps下项目
以前没想过这么个问题:Tomcat怎么处理webapps下项目,并且我访问浏览器ip: port/项目名/请求路径,以SSM为例,Tomcat怎么就能将请求找到项目呢,项目还是个文件夹类型的? Tom ...
- Tomcat8源码笔记(七)组件启动Server Service Engine Host启动
一.Tomcat启动的入口 Tomcat初始化简单流程前面博客介绍了一遍,组件除了StandardHost都有博客,欢迎大家指文中错误.Tomcat启动类是Bootstrap,而启动容器启动入口位于 ...
- 05 flask源码剖析之配置加载
05 Flask源码之:配置加载 目录 05 Flask源码之:配置加载 1.加载配置文件 2.app.config源码分析 3.from_object源码分析 4. 总结 1.加载配置文件 from ...
- 从SpringBoot源码分析 配置文件的加载原理和优先级
本文从SpringBoot源码分析 配置文件的加载原理和配置文件的优先级 跟入源码之前,先提一个问题: SpringBoot 既可以加载指定目录下的配置文件获取配置项,也可以通过启动参数( ...
- 【Spring源码分析】Bean加载流程概览
代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...
随机推荐
- redis在游戏服务器中的使用初探(一) 环境搭建
这里我们尝试在游戏服务器中的数据处理中使用redis 通过该系列文章能够学习 redis的基本操作 源码编译 客户端开源库的编译和使用 以及在游戏服务器中的缓存使用 作为初次摸索 尽量使得环境简单 ...
- Minimum Domino Rotations For Equal Row LT1007
In a row of dominoes, A[i] and B[i] represent the top and bottom halves of the i-th domino. (A domi ...
- Android 常用RGB值以及中英文名称
Android 常用RGB值以及中英文名称 Android 常用 RGB值以及中英文名称 颜 色 RGB 值 英文名 中文名 #FFB6C1 LightPink 浅 ...
- java内存性能调优编码注意
1.没有必要时请不用使用静态变量 使用Java的开发者都知道,当某个对象被定义为stataic变量所引用,这个对象所占有的内存将不会被回收.有时,开发者会将经常调用的对象或者变量定义为static,以 ...
- java 支持 超大上G , 多附件上传
首先 确定要上传的目录 WEB.XML 文件 Java代码 <listener> <listener-class><!-- 临时文件收集器 , 支持超大附件必须项 - ...
- Reading | 《Python基础教程》第1次阅读
目录 一.基础知识 1.数和表达式 浮点除法和整数除法 负数整除和取余 圆整 乘方运算 2.变量名 3.获取用户输入 4.模块 5.让脚本像普通程序一样 6.字符串 单.双引号 引号的转义 字符串拼接 ...
- Chapter6 胞内信号网络
一.一条从细胞表面到细胞核的通路 二.Ras蛋白处于复杂信号级联的中心位置 胞外信号→酪氨酸激酶受体→Shc→Grb→Sos→Ras 三.酪氨酸的磷酸化控制着许多胞内信号蛋白的定位与活动 Src蛋白的 ...
- ELK学习
[root@log-node1 ~]# cobbler repo add --name=logstash-2.3 --mirror=http://packages.elastic.co/logstas ...
- Pycharm 开发 Django 项目
1. 安装Pycharm, 自行百度安装教程 2. 安装python3 自行百度安装教程 3. 安装Django框架 使用命令: Window的终端控制台输入:安装Django框架. pip inst ...
- Maven的多mirrors的配置
repo1 central Human Readable Name for this Mirror. http://repo1.maven.org/maven2/ repo2 central Huma ...