引言

在执行catalina的load方法时,会执行配置Digester、读取配置文件、将Catalina作为digester的顶级容器、digester解析配置文件并注入各个组件。
简略修改了一下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Create and execute our Digester
//创建和配置将用于启动的Digester。
//配置解析server.xml中各个标签的解析类
Digester digester = createStartDigester();

//下面一大段都是为了conf/server.xml配置文件,失败就加载server-embed.xml
File file = configFile();
InputSource inputStream = new FileInputStream(file);
InputSource inputSource = new InputSource(file.toURI().toURL().toString());
inputSource.setByteStream(inputStream);

//把Catalina作为一个顶级容器
digester.push(this);
//解析过程会实例化各个组件,比如Server、Container、Connector等
digester.parse(inputSource);

Digeter是apache的common项目,作用是将XML转化成对象,使用者直接从对象中获取xml的节点信息。
Digester是对SAX的包装,它也是基于文件流来解析xml文件,只不过这些解析操作对用户是透明的。
在分析createStartDigester之前很有必要贴一下server.xml配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
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" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Host>
</Engine>
</Service>
</Server>

这个是默认配置,我将注释之类去除了。

createStartDigester

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/**
* 创建和配置将用于启动的Digester。
*
* @return the main digester to parse server.xml
*/
protected Digester createStartDigester() {
long t1 = System.currentTimeMillis();
// Initialize the digester
Digester digester = new Digester();
//设置验证解析器标志。
digester.setValidating(false);
//设置规则验证标志。
digester.setRulesValidation(true);
//假的属性
Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
List<String> attrs = new ArrayList<>();
attrs.add("className");
fakeAttributes.put(Object.class, attrs);
//设置假属性。
digester.setFakeAttributes(fakeAttributes);
//确定是否使用上下文类加载器来解析/加载在各种规则中定义的类。
digester.setUseContextClassLoader(true);

// 配置我们将使用的操作
//addObjectCreate:为指定的参数添加“对象创建”规则。
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
//为指定的参数添加“设置属性”规则。
digester.addSetProperties("Server");
//为指定的参数添加“set next”规则。
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");

// StandardServer.GlobalNamingResources
digester.addObjectCreate("Server/GlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources",
"setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");

//StandardServer.addLifecycleListener
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");

//StandardServer.addService:StandardService
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service");

//StandardService.addLifecycleListener
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
//StandardService.addExecutor
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");


//StandardService.addConnector
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(
new String[]{"executor", "sslImplementationName", "protocol"}));
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector");

//Connector.addSslHostConfig
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");

//SSLHostConfig.addCertificate
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");

//SSLHostConfig.setOpenSslConf
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");

//OpenSSLConfCmd.addCmd
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");

//Connector.addLifecycleListener
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");

//Connector.addUpgradeProtocol
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/");

long t2 = System.currentTimeMillis();
if (log.isDebugEnabled()) {
log.debug("Digester for server.xml created " + (t2 - t1));
}
return digester;

}

先简单介绍一下Digester方法:

  • addObjectCreate
    创建对象
  • addSetProperties
    设置属性
  • addSetNext
    创建对象之间关系
  • addRule
    该方法会将一个Rule对象和它所匹配的模式添加到Digester对象的Rules集合中
  • addRuleSet
    调用addRuleInstances来解析xml标签

上述所有的步骤都是为了解析XML而准备,解析时根据XML内各标签的包含关系来创建对象,设置属性,关联对象。
备注:当前栈顶元素为Catalina实例。
下面简单看几个标签的解析规则:

Server

1
2
3
4
5
6
7
8
9
10
//addObjectCreate:为指定的参数添加“对象创建”规则。
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
//为指定的参数添加“设置属性”规则。
digester.addSetProperties("Server");
//为指定的参数添加“set next”规则。
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");

创建StandardServer实例对象,这是属性,调用上一个元素Catalina的setServer方法。
现在栈顶元素为StandardServer实例对象,也就是Server的外层元素是Catalina。

1
2
3
public void setServer(Server server) {
this.server = server;
}

Service

1
2
3
4
5
6
7
8
9
//StandardServer.addService:StandardService
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service");

会先创建StandardService实例,属性填充,并找到上层标签Server的实例StandardServer,执行addService方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Override
public void addService(Service service) {
//service持有Server的引用
service.setServer(this);
//服务锁,即service要一个一个的加入
synchronized (servicesLock) {
//services数组拷贝,将新来的service增加到最后
Service results[] = new Service[services.length + 1];
System.arraycopy(services, 0, results, 0, services.length);
results[services.length] = service;
services = results;
//当前Server组件是available
if (getState().isAvailable()) {
try {
//调用service的生命周期方法start
service.start();
} catch (LifecycleException e) {
// Ignore
}
}
// Report this property change to interested listeners
//将此属性更改报告给感兴趣的侦听器
support.firePropertyChange("service", null, service);
}
}

可以看出Server与Service是一对多的关系。

Connector

1
2
3
4
5
6
7
8
//StandardService.addConnector
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(
new String[]{"executor", "sslImplementationName", "protocol"}));
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector");

这里是通过ConnectorCreateRule来解析Connector节点,ConnectorCreateRule实现了Rule接口,解析的时候先执行begin方法,获取
digester的上层元素Service的实例StandardService,再创建Connector实例,设置一些属性之后将Connector入digester栈。解析结束后
调用end方法,将Connector实例出栈。也就是说当前栈顶是Service实例,执行StandardService的addConnector方法建立联系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void addConnector(Connector connector) {
//connector一个一个加入到当前Service组件中
synchronized (connectorsLock) {
connector.setService(this);
Connector results[] = new Connector[connectors.length + 1];
System.arraycopy(connectors, 0, results, 0, connectors.length);
results[connectors.length] = connector;
connectors = results;
if (getState().isAvailable()) {
try {
//生命周期函数
connector.start();
} catch (LifecycleException e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connector), e);
}
}
// Report this property change to interested listeners
support.firePropertyChange("connector", null, connector);
}
}

从这可以看出Service组件与Connector组件是一对多的关系。

Engine

1
digester.addRuleSet(new EngineRuleSet("Server/Service/"));

EngineRuleSet实现了RuleSet接口,当解析xml时会调用addRuleInstances方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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");
}

套路还是一样的,先解析一层,之后再返回上一层。最终会执行到StandardService的setContainer方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public void setContainer(Engine engine) {
//废掉老的Engine
Engine oldEngine = this.engine;
if (oldEngine != null) {
oldEngine.setService(null);
}
//绑定新的engine
this.engine = engine;
if (this.engine != null) {
this.engine.setService(this);
}
if (getState().isAvailable()) {
if (this.engine != null) {
try {
//生命周期函数
this.engine.start();
} catch (LifecycleException e) {
log.warn(sm.getString("standardService.engine.startFailed"), e);
}
}
// Restart MapperListener to pick up new engine.
//重启MapperListener以获取新引擎。
try {
mapperListener.stop();
} catch (LifecycleException e) {
log.warn(sm.getString("standardService.mapperListener.stopFailed"), e);
}
try {
mapperListener.start();
} catch (LifecycleException e) {
log.warn(sm.getString("standardService.mapperListener.startFailed"), e);
}
//关闭老的引擎
if (oldEngine != null) {
try {
oldEngine.stop();
} catch (LifecycleException e) {
log.warn(sm.getString("standardService.engine.stopFailed"), e);
}
}
}

// Report this property change to interested listeners
support.firePropertyChange("container", oldEngine, this.engine);
}

可以看出Service与Engine引擎是一对一的关系。

Host

1
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));

能看出来Host在Engine的下一层。最终会执行StandardEngine的addChild方法,将Host容器置于Engine内部。

Context

1
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));

Context在Host的下一层,也会执行StandardHost的addChild方法,将Context容器置于Host容器内部。

发现没,这里没涉及到Wrapper容器,它是什么时候产生的呢?
这里先保留,等后面遇到再说。

至此,server.xml配置文件的解析差不多就了解了,也知道了Catalina中的Server组件是怎么注入的,还了解了Server、Service、Engine、Host、Context之间的关系。