上篇文章我们介绍到,在Catalina的load方法中,完成了server.xml的解析,将server.xml配置的Server、Service、Connector、Engine、Host各组件实例化,并维护父子级关系。本篇文章我们来看一下server.xml是如何解析的。
不难看到,parseServerXml方法执行后,各Tomcat组件就已经生成了。
1. parseServerXml
核心的解析逻辑就在上图38~49行:
Digester digester = start ? createStartDigester() : createStopDigester();
InputStream inputStream = resource.getInputStream();
InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource);
创建一个 Digester对象,将当前对象压入Digester 里的对象栈顶,根据 inputSource里对应的文件xml路径及所创建的Digester对象所包含的解析规则生成相应对象,并调用相应方法将对象之间关联起来。这里我们引入一个新的概念Digester,Digester就是用来解析xml的。
2. XML文件解析方法
Java解析XML文件主要存在两种方式:预加载DOM树及事件机制的SAX。
2.1 预加载DOM树
将整个XML文件读取到内存中,在内容中构造一个DOM树,然后java代码只需要操作这个树就可以。该方法的主要实现为DOM解析,在此基础上有两个扩展:JDOM解析,DOM4J解析,DOM4J更常用一些。
- 优点:使用DOM时,XML文档的结构在内存中很清晰,元素与元素之间的关系保留了下来,即能记录文档的所有信息
- 缺点:如果XML文档非常大,把整个XML文档装在进内存容易造成内容溢出,无法加载了
2.2 事件机制的SAX
一行一行的读取XML文件,每遇到一个节点,就看看有没有监听该事件的监听器,如果有就触发。当XML文件读取结束后,内存里不会保存任何数据,整个XML的解析就结束了。所以,这里面最重要的是对感兴趣的节点进行监听和处理。
- 优点:使用SAX不会占用大量内存来保存XML文档数据,效率高
- 缺点:不像DOM一样将文档树长期留驻在内存,数据不是长久的。事件过后,若没保存数据,那么数据就会丢失
3. Digester
3.1 Digester基本架构
- DefaultHandler2:来自SAX,Digester继承该类,说明Digester底层用的是SAX
- Digester:Digester解析的核心类和入口类,客户端(需要解析xml的调用方)需要实例化该类,设置该类,并调用该类的方法
- Rules:保存XML的节点和规则的映射关系,默认实现类是RulesBase
- RuesBase:是Rules的默认实现类。当有一个XML节点开始解析时,会在这里面找是否有对应的节点,并根据节点查找对应的处理规则
- Rule:节点的处理方法,内置的或者自定义的规则都是继承自该接口
3.2 Digester解析XML文档的流程
- Client需要创建一个Digester对象
- Client必须根据自己的XML格式来添加所有的Rule
- Client调用Digester的parse操作来解析XML文件。
- Digester实现了SAX的接口,解析时遇到具体的XML对象时会调用startElement(继承自DefaultHandler2)等方法
- 在这些SAX接口函数中,会扫描规则链(RulesBase),找到匹配规则,规则匹配一般都是根据具体的元素名称来进行匹配
- 找到对应的Rule后,依次执行Rule
- 文档结束后,会执行所有Rule的finish函数
3.3 使用流程
Digester digester = createStartDigester();
File file = configFile();
InputStream inputStream = new FileInputStream(file);
InputSource inputSource = new InputSource(file.toURI().toURL().toString());
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource);
- 准备用来解析server.xml文件需要用的digester
- 读取server.xml文件作为一个输入流
- 使用inputStream构造一个sax的inputSource,因为digester底层用的是sax去解析的
- 把当前类压入到digester的栈顶,用来作为digester解析出来的对象的一种引用,digester自带一个栈的结构
- 调用digester的parse()方法进行解析。前面几步都是在准备环境,这里才是正真的去解析了
4 Digester解析xml示例
下面我们使用digester解析一段我们自定义的xml。首先我们需要引入依赖包:
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-digester3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-digester3</artifactId>
<version>3.2</version>
</dependency>
4.1 自定义xml
我们通过xml表达学校、年级、班级这三层概念:
- 一个学校有名字属性,下面有多个年级
- 每个年级有名字属性,下面有多个班
- 每个班有名字和学生人数两个属性
我们自定义一个school.xml文件,如下:
<?xml version='1.0' encoding='utf-8'?>
<School name="Shiyan">
<Grade name="1">
<Class name="1" number="31"/>
<Class name="2" number="32"/>
</Grade>
<Grade name="2">
<Class name="1" number="41"/>
<Class name="2" number="42"/>
<Class name="3" number="37"/>
</Grade>
</School>
4.2 自定义班级Class对象
public class Class {
private String name;
private int number;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
跟上面xml对应,有名称和学生人数两个属性。
4.3 自定义年级Grade对象
public class Grade {
private String name;
private Class[] classes = new Class[0];
private final Object servicesLock = new Object();
public void addClass(Class c) {
synchronized (servicesLock) {
Class[] results = new Class[classes.length + 1];
System.arraycopy(classes, 0, results, 0, classes.length);
results[classes.length] = c;
classes = results;
}
}
public Class[] getClasses() {
return classes;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
每个年级有名称属性,并且包含多个班级。
4.4 自定义学校School属性
public class School {
private String name;
private Grade[] grades = new Grade[0];
private final Object servicesLock = new Object();
public void addGrade(Grade g) {
synchronized (servicesLock) {
Grade[] results = new Grade[grades.length + 1];
System.arraycopy(grades, 0, results, 0, grades.length);
results[grades.length] = g;
grades = results;
}
}
public Grade[] getGrades() {
return grades;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
学校有名称属性,并且包含多个年级。
4.5 使用Degister解析xml
public class DigesterTest {
// 属性和get/set方法,假设我们解析出来的School对象放在这儿
private School school;
public School getSchool() {
return school;
}
public void setSchool(School s) {
this.school = s;
}
private void digester() throws IOException, SAXException {
// 读取根据文件的路径,创建InputSource对象,digester解析的时候需要用到
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("school.xml");
InputSource inputSource = new InputSource();
inputSource.setByteStream(inputStream);
// 创建Digester对象
Digester digester = new Digester();
// 是否需要用DTD验证XML文档的合法性
//digester.setValidating(true);
// 将当前对象放到对象堆的最顶层,这也是这个类为什么要有school属性的原因!
digester.push(this);
/*
* 下面开始为Digester创建匹配规则
* Digester中的School、School/Grade、School/Grade/Class,分别对应School.xml的School、Grade、Class节点
*/
// 为School创建规则
/*
* Digester.addObjectCreate(String pattern, String className, String attributeName)
* pattern, 匹配的节点
* className, 该节点对应的默认实体类
* attributeName, 如果该节点有attributeName属性, 用attributeName属性对应的值替换默认实体类
*
* Digester匹配到School节点
*
* 1. 如果School节点没有className属性,将创建com.zhuoli.service.digester.School对象;
* 2. 如果School节点有className属性,将创建指定的(className属性的值)对象
*/
digester.addObjectCreate("School", School.class.getName(), "className");
// 将指定节点的属性映射到对象,即将School节点的name的属性映射到School.java
digester.addSetProperties("School");
/*
* Digester.addSetNext(String pattern, String methodName, String paramType)
* pattern, 匹配的节点
* methodName, 调用父节点的方法
* paramType, 父节点的方法接收的参数类型
* Digester匹配到School节点,将调用DigesterTest(School的父节点)的setSchool方法,参数为School对象
*/
digester.addSetNext("School", "setSchool", School.class.getName());
// 为School/Grade创建规则
digester.addObjectCreate("School/Grade", Grade.class.getName(), "className");
digester.addSetProperties("School/Grade");
// Grade的父节点为School
digester.addSetNext("School/Grade", "addGrade", Grade.class.getName());
// 为School/Grade/Class创建规则
digester.addObjectCreate("School/Grade/Class", Class.class.getName(), "className");
digester.addSetProperties("School/Grade/Class");
digester.addSetNext("School/Grade/Class", "addClass", Class.class.getName());
// 解析输入源
digester.parse(inputSource);
}
// 只是将School对象进行控制台输出
private void print(School s) {
if (s != null) {
System.out.println(s.getName() + "有" + s.getGrades().length + "个年级");
for (int i = 0; i < s.getGrades().length; i++) {
if (s.getGrades()[i] != null) {
Grade g = s.getGrades()[i];
System.out.println(g.getName() + "年级 有 " + g.getClasses().length + "个班:");
for (int j = 0; j < g.getClasses().length; j++) {
if (g.getClasses()[j] != null) {
Class c = g.getClasses()[j];
System.out.println(c.getName() + "班有" + c.getNumber() + "人");
}
}
}
}
}
}
public static void main(String[] args) throws IOException, SAXException {
DigesterTest digesterTest = new DigesterTest();
digesterTest.digester();
digesterTest.print(digesterTest.school);
}
}
运行结果:
4.6 Degister使用总结
Degister总体上,我们可以将其方法分为两类:操作类和规则类。
- 操作类
- void setValidating(boolean validating):是否根据DTD校验XML
- void push(Object object):将对象压入栈
- Object peek():获取栈顶对象
- Object pop():弹出栈顶对象
- Object parse(InputSource input):解析输入源
- 规则类
- void addObjectCreate(String pattern, String className, String attributeName):增加对象创建规则,当匹配到pattern模式时,如果指定了attributeName,则根据attributeName创建类对象;否则根据className创建类对象
- void addSetProperties(String pattern):增加属性设置规则,当匹配到pattern模式时,就填充其属性
- void addSetNext(String pattern, String methodName, String paramType):增加设置下一个规则,当匹配到pattern模式时,调用父节点的methodName方法,paramType为方法传入参数的类型
- void addRule(String pattern, Rule rule):当匹配到pattern模式时,增加一个自定义规则
- void addRuleSet(RuleSet ruleSet):增加规则集,一个规则集指的是对一个节点及下面的所有子节点(子节点、子节点的子节点…)的解析
5. Tomcat server.xml解析
5.1 server.xml
5.2 Degister创建
protected Digester createStartDigester() {
// Initialize the digester
Digester digester = new Digester();
digester.setValidating(false);
digester.setRulesValidation(true);
Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
// Ignore className on all elements
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);
// Ignore Connector attribute used internally but set on Server
List<String> connectorAttrs = new ArrayList<>();
connectorAttrs.add("portOffset");
fakeAttributes.put(Connector.class, connectorAttrs);
digester.setFakeAttributes(fakeAttributes);
digester.setUseContextClassLoader(true);
// Configure the actions we will be using
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
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.addRule("Server/Listener",
new ListenerCreateRule(null, "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.addSetProperties("Server/Service/Connector",
new String[]{"executor", "sslImplementationName", "protocol"});
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector");
digester.addRule("Server/Service/Connector", new AddPortOffsetRule());
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");
digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
new CertificateCreateRule());
digester.addSetProperties("Server/Service/Connector/SSLHostConfig/Certificate", new String[]{"type"});
digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate",
"addCertificate",
"org.apache.tomcat.util.net.SSLHostConfigCertificate");
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");
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");
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");
digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",
null, // MUST be specified in the element
"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/");
return digester;
5.3 Server解析
5.3.1 创建Server实例
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("Server");
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
创建StandardServer对象,设置其对象的属性,调用父节点Catalina的setServer方法将Server添加到Catalina中。
5.3.2 为Server添加全局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中。
5.3.4 为Server添加生命周期监听器
digester.addRule("Server/Listener",
new ListenerCreateRule(null, "className"));
digester.addSetProperties("Server/Listener");
digester.addSetNext("Server/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
当匹配到”Server/Listener”节点,使用ListenerCreateRule解析。注意ListenerCreateRule第一个参数是className,即默认类名,为null。所以Listener不会有默认值类型。因为Listener本来就是自定义的。
比如在server.xml中,为Server添加如下监听器:
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<!-- Security listener. Documentation at /docs/config/listeners.html
<Listener className="org.apache.catalina.security.SecurityListener" />
-->
<!--APR library loader. Documentation at /docs/apr.html -->
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
VersionLoggerListener
:在Server初始化之前打印操作系统,JVM以及服务器的版本信息AprLifecycleListener
:在Server初始化之前加载APR库,并于Server停止之后销毁JreMemoryLeakPreventionListener
:在Server初始化之前调用,以解决单例对象创建导致的JVM内存泄漏问题以及锁文件问题GlobalResourcesLifecycleListener
:在Server启动时,将JNDI资源注册为MBean进行管理ThreadLocalLeakPreventionListener
:用于在Context停止时重建Exceutor池中的线程,避免导致内存泄漏
5.4 Servive解析
5.4.1 创建Service实例
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service");
创建StandardService实例,调用StandardService实例的set方法设置属性,最后调用父节点Server的addService方法,将service添加到Server。
5.4.2 为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");
这里创建生命周期监听器,className上送值为null,所以不存在默认类型,比如有”Server/Service/Listener”节点的className指定类型。
随后调用父节点Service的addLifecycleListener方法,将监听器添加到Service。
5.5 Executor解析
//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");
创建StandardThreadExecutor实例,并调用父节点Service的addExecutor方法添加到Service。通过该配置我们可以知道,Tomcat共享Excetor的级别为Service,Catalina默认情况下未配置Executor,即不共享。
5.6 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");
创建Connector实例,并调用父组建Service的addConnector方法添加到Service。设置相关属性时,将executor和sslImplementationName属性排除,因为在Connector创建时,会判断当前是否指定了executor属性,如果是,则从Service中查找该名称的executor并设置到Connector中。同样,Connector创建时,也会判断是否添加了sslIlplementationName属性,如果是,则将属性值设置到使用的协议中,为其指定一个SSL实现。
5.6.1 为Connector添加虚拟主机SSL配置
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");
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");
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");
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");
5.6.2 为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");
5.6.3 为Connector添加升级协议
digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");
digester.addSetNext("Server/Service/Connector/UpgradeProtocol",
"addUpgradeProtocol",
"org.apache.coyote.UpgradeProtocol");
用于支持HTTP/2。
5.7 容器组件解析
// 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/"));
此部分指定了Servlet容器相关的各级嵌套子节点的解析规则,而且没类嵌套子节点的解析规则封装为一个RuleSet,包括GlobalNamingResources,Engine,Host,Context以及Cluster解析。
5.7.1 Engine解析
Engine解析是在digester.addRuleSet(new EngineRuleSet("Server/Service/"));
方法中完成的。最终EngineRuleSet的addRuleInstances会生效,将规则添加到Digester,如下所示:
public void addRuleInstances(Digester digester) {
digester.addObjectCreate(prefix + "Engine",
"org.apache.catalina.core.StandardEngine",
"className");
digester.addSetProperties(prefix + "Engine");
digester.addRule(prefix + "Engine",
new LifecycleListenerRule
("org.apache.catalina.startup.EngineConfig",
"engineConfigClass"));
digester.addSetNext(prefix + "Engine",
"setContainer",
"org.apache.catalina.Engine");
//Cluster configuration start
digester.addObjectCreate(prefix + "Engine/Cluster",
null, // MUST be specified in the element
"className");
digester.addSetProperties(prefix + "Engine/Cluster");
digester.addSetNext(prefix + "Engine/Cluster",
"setCluster",
"org.apache.catalina.Cluster");
//Cluster configuration end
digester.addObjectCreate(prefix + "Engine/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties(prefix + "Engine/Listener");
digester.addSetNext(prefix + "Engine/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
digester.addRuleSet(new RealmRuleSet(prefix + "Engine/"));
digester.addObjectCreate(prefix + "Engine/Valve",
null, // MUST be specified in the element
"className");
digester.addSetProperties(prefix + "Engine/Valve");
digester.addSetNext(prefix + "Engine/Valve",
"addValve",
"org.apache.catalina.Valve");
}
可以看到,该方法中完成了如下几项工作:
- 创建实例,并通过setContainer添加Service中
- 为Engine添加了一个生命周期的监听器EngineConfig,这个监听器是代码里写死,不是通过server.xml配置的
- 为Engine添加集群配置
- 为Engine添加生命周期监听器,这里的监听器是server.xml文件中配置的
- 为Engine添加Valve,具体的拦截器由className属性指定,不指定默认值
5.7.2 Host的解析
Host的解析是在digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
方法中完成的。最终HostRuleSet的addRuleInstances会生效,将规则添加到Digester,如下所示:
public void addRuleInstances(Digester digester) {
digester.addObjectCreate(prefix + "Host",
"org.apache.catalina.core.StandardHost",
"className");
digester.addSetProperties(prefix + "Host");
digester.addRule(prefix + "Host",
new CopyParentClassLoaderRule());
digester.addRule(prefix + "Host",
new LifecycleListenerRule
("org.apache.catalina.startup.HostConfig",
"hostConfigClass"));
digester.addSetNext(prefix + "Host",
"addChild",
"org.apache.catalina.Container");
digester.addCallMethod(prefix + "Host/Alias",
"addAlias", 0);
//Cluster configuration start
digester.addObjectCreate(prefix + "Host/Cluster",
null, // MUST be specified in the element
"className");
digester.addSetProperties(prefix + "Host/Cluster");
digester.addSetNext(prefix + "Host/Cluster",
"setCluster",
"org.apache.catalina.Cluster");
//Cluster configuration end
digester.addObjectCreate(prefix + "Host/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties(prefix + "Host/Listener");
digester.addSetNext(prefix + "Host/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
digester.addRuleSet(new RealmRuleSet(prefix + "Host/"));
digester.addObjectCreate(prefix + "Host/Valve",
null, // MUST be specified in the element
"className");
digester.addSetProperties(prefix + "Host/Valve");
digester.addSetNext(prefix + "Host/Valve",
"addValve",
"org.apache.catalina.Valve");
}
该方法中完成了如下几项工作:
- 创建Host实例,通过addChild()方法添加到Engine上
- 为Host添加了一个生命周期监听器HostConfig,这个监听器是代码里写死,不是通过server.xml配置的。这里的HostConfig在后面的文章介绍Context构建时,会重点提到
- 为Host配置集群,所以集群的配置即可以在Engine级别,也可以在Host级别
- 为Host添加生命周期监听器,这里的监听器是server.xml文件中配置的
- 为Host添加Valve,具体的拦截器由className属性指定,不指定默认值
5.7.3 Context解析
多数情况下,我们并不需要在server.xml中配置Context,而是由HostConfig自动扫描部署目录,以context.xml文件为基础进行解析创建。
Context的解析,是在digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
方法中完成的,最终ContextRuleSet的addRuleInstances会生效,将规则添加到Digester,如下所示:
public void addRuleInstances(Digester digester) {
// 1. 创建Context实例,通过server.xml配置Context时,create是true,需要创建Context实例;通过HostConfig创建Context时,create为false,此时仅需要解析节点即可
if (create) {
digester.addObjectCreate(prefix + "Context",
"org.apache.catalina.core.StandardContext", "className");
digester.addSetProperties(prefix + "Context");
} else {
digester.addSetProperties(prefix + "Context", new String[]{"path", "docBase"});
}
if (create) {
digester.addRule(prefix + "Context",
new LifecycleListenerRule
("org.apache.catalina.startup.ContextConfig",
"configClass"));
digester.addSetNext(prefix + "Context",
"addChild",
"org.apache.catalina.Container");
}
// 2. 为Context添加生命周期监听器
digester.addObjectCreate(prefix + "Context/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties(prefix + "Context/Listener");
digester.addSetNext(prefix + "Context/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
// 3. 为Context指定类加载器,默认为org.apache.catalina.loader.WebappLoader
digester.addObjectCreate(prefix + "Context/Loader",
"org.apache.catalina.loader.WebappLoader",
"className");
digester.addSetProperties(prefix + "Context/Loader");
digester.addSetNext(prefix + "Context/Loader",
"setLoader",
"org.apache.catalina.Loader");
// 4. 为Context添加会话管理器,默认实现为StandardManager
digester.addObjectCreate(prefix + "Context/Manager",
"org.apache.catalina.session.StandardManager",
"className");
digester.addSetProperties(prefix + "Context/Manager");
digester.addSetNext(prefix + "Context/Manager",
"setManager",
"org.apache.catalina.Manager");
digester.addObjectCreate(prefix + "Context/Manager/Store",
null, // MUST be specified in the element
"className");
digester.addSetProperties(prefix + "Context/Manager/Store");
digester.addSetNext(prefix + "Context/Manager/Store",
"setStore",
"org.apache.catalina.Store");
digester.addObjectCreate(prefix + "Context/Manager/SessionIdGenerator",
"org.apache.catalina.util.StandardSessionIdGenerator",
"className");
digester.addSetProperties(prefix + "Context/Manager/SessionIdGenerator");
digester.addSetNext(prefix + "Context/Manager/SessionIdGenerator",
"setSessionIdGenerator",
"org.apache.catalina.SessionIdGenerator");
//5. 为Context添加初始化参数,通过该配置,为Context添加初始化参数
digester.addObjectCreate(prefix + "Context/Parameter",
"org.apache.tomcat.util.descriptor.web.ApplicationParameter");
digester.addSetProperties(prefix + "Context/Parameter");
digester.addSetNext(prefix + "Context/Parameter",
"addApplicationParameter",
"org.apache.tomcat.util.descriptor.web.ApplicationParameter");
// 6. 为Context添加安全配置以及web资源配置
digester.addRuleSet(new RealmRuleSet(prefix + "Context/"));
digester.addObjectCreate(prefix + "Context/Resources",
"org.apache.catalina.webresources.StandardRoot",
"className");
digester.addSetProperties(prefix + "Context/Resources");
digester.addSetNext(prefix + "Context/Resources",
"setResources",
"org.apache.catalina.WebResourceRoot");
digester.addObjectCreate(prefix + "Context/Resources/PreResources",
null, // MUST be specified in the element
"className");
digester.addSetProperties(prefix + "Context/Resources/PreResources");
digester.addSetNext(prefix + "Context/Resources/PreResources",
"addPreResources",
"org.apache.catalina.WebResourceSet");
digester.addObjectCreate(prefix + "Context/Resources/JarResources",
null, // MUST be specified in the element
"className");
digester.addSetProperties(prefix + "Context/Resources/JarResources");
digester.addSetNext(prefix + "Context/Resources/JarResources",
"addJarResources",
"org.apache.catalina.WebResourceSet");
digester.addObjectCreate(prefix + "Context/Resources/PostResources",
null, // MUST be specified in the element
"className");
digester.addSetProperties(prefix + "Context/Resources/PostResources");
digester.addSetNext(prefix + "Context/Resources/PostResources",
"addPostResources",
"org.apache.catalina.WebResourceSet");
// 7. 为Context添加资源连接,默认为ContextResourceLink,用于J2EE命名服务
digester.addObjectCreate(prefix + "Context/ResourceLink",
"org.apache.tomcat.util.descriptor.web.ContextResourceLink");
digester.addSetProperties(prefix + "Context/ResourceLink");
digester.addRule(prefix + "Context/ResourceLink",
new SetNextNamingRule("addResourceLink",
"org.apache.tomcat.util.descriptor.web.ContextResourceLink"));
// 8. 为Context添加Valve
digester.addObjectCreate(prefix + "Context/Valve",
null, // MUST be specified in the element
"className");
digester.addSetProperties(prefix + "Context/Valve");
digester.addSetNext(prefix + "Context/Valve",
"addValve",
"org.apache.catalina.Valve");
// 9. 为Context添加守护资源配置
digester.addCallMethod(prefix + "Context/WatchedResource",
"addWatchedResource", 0);
digester.addCallMethod(prefix + "Context/WrapperLifecycle",
"addWrapperLifecycle", 0);
digester.addCallMethod(prefix + "Context/WrapperListener",
"addWrapperListener", 0);
digester.addObjectCreate(prefix + "Context/JarScanner",
"org.apache.tomcat.util.scan.StandardJarScanner",
"className");
digester.addSetProperties(prefix + "Context/JarScanner");
digester.addSetNext(prefix + "Context/JarScanner",
"setJarScanner",
"org.apache.tomcat.JarScanner");
digester.addObjectCreate(prefix + "Context/JarScanner/JarScanFilter",
"org.apache.tomcat.util.scan.StandardJarScanFilter",
"className");
digester.addSetProperties(prefix + "Context/JarScanner/JarScanFilter");
digester.addSetNext(prefix + "Context/JarScanner/JarScanFilter",
"setJarScanFilter",
"org.apache.tomcat.JarScanFilter");
// 10. 为Context添加Cookie处理器
digester.addObjectCreate(prefix + "Context/CookieProcessor",
"org.apache.tomcat.util.http.Rfc6265CookieProcessor",
"className");
digester.addSetProperties(prefix + "Context/CookieProcessor");
digester.addSetNext(prefix + "Context/CookieProcessor",
"setCookieProcessor",
"org.apache.tomcat.util.http.CookieProcessor");
}
通过如上Degister机制,我们实现了将server.xml解析的过程,输出结果就是相关的Tomcat的组件被实例化出来,并且维护好了组件的上下级关系。后续我们调用init方法和start方法,也是针对这些已经实例化后的组件展开的。
参考链接:
1. Digester组件
4. Tomcat源码