Tomcat源码分析 -- Server的创建
本篇结构:
- 前言
- server的解析
- Engine的解析
- Host的解析
- Context的解析
- 总结
一、前言
前面已经介绍了Tomcat服务器的整体结构和启动过程,本篇将聚焦server.xml的解析,介绍Server的整个创建过程。
二、server的解析
要了解Server的创建,就需要回到前面关于Tomcat启动过程文章中介绍的Catalina中的load方法,在load方法中会解析conf/server.xml配置文件。
前面有提到,Tomcat是使用Digester解析server.xml的,Digester的解析规则是在Catalina类的createStartDigester方法中。
简单分析下该方法。
// 首先实例化一个Digester对象
Digester digester = new Digester();
// 设置为false表示解析xml时不需要进行DTD的规则校验
digester.setValidating(false);
// 是否进行节点设置规则校验,如果xml中相应节点没有设置解析规则会在控制台显示提示信息
digester.setRulesValidation(true);
2.1、创建Server实例
// 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");
默认的Server实现类是org.apache.catalina.core.StandardServer,也可以在server.xml的server标签上配置className指定自己的实现。创建Server实例后,然后设置Server相关属性,然后调用栈顶对象(即该Server)之后的对象(是前面通过push(this)放入的Catalina实例)的setServer方法,将server实例设置到Catalina对象中。
digester.addObjectCreate("Server/GlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources",
"setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");
然后以同样的规则创建J2EE企业命名上下文(JNDI),设置属性并将其设置到Server实例中。
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");
接着为Server实例添加生命周期监听器,默认的server.xml中配置了5个生命周期监听器。
<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"/>
也可以自己写一个监听器,在server.xml中配置:
public class MyListener implements LifecycleListener {
@Override
public void lifecycleEvent(LifecycleEvent event) {
if (Lifecycle.START_EVENT.equals(event.getType())){
test();
}
}
private void test() {
System.out.println("====================================");
System.out.println("test test test");
System.out.println("====================================");
}
}
启动就能看到打印信息:
2.2、创建Service实例
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service");
创建Service实例,默认是org.apache.catalina.core.StandardService,然后通过addService设置到Server实例中。
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");
addLifecycleListener为Service增加生命周期监听器,默认情况下,service没指定监听器。
//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");
创建Executor实例,默认为org.apache.catalina.core.StandardThreadExecutor,并将其设置到Service实例中,默认情况下,未配置Executor。
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");
为Service实例添加Connector。在设置Connector相关属性时,会判断server.xml中是否指定了executor属性,如果是,会从Service中查找该名称的Executor设置到Connector中。同时,在创建Connector时,也会判断是否有sslImplementationName属性,如果有,设置到使用的协议中,为其指定一个SSL实现。
接着是一大段为Connector添加虚拟主机SSL配置相关的代码,不是很懂这些,就不列出了。
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添加监听器,默认未指定。
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");
为Connector添加添加升级协议。
// 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/");
最后这部分代码指定了Servlet容器相关的各级嵌套子节点的解析规则,它将每类子节点的解析封装为一个RuleSet包括GlobalNamingResources,Engine,Host,Context及Cluster的解析,这里重点关注Engine和Host的解析,它们的解析和Context密切相关。
三、Engine的解析
Engine的解析过程在EngineRuleSet类中。
3.1、创建Engine实例
//EngineRuleSet.addRuleInstances
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");
创建Engine实例,默认是org.apache.catalina.core.StandardEngine,为其设置属性。接着会给Engine添加一个生命周期监听器,默认是org.apache.catalina.startup.EngineConfig,用于打印Engine启动和停止日志,然后将该Engine实例设置到Service中。
需要说明的是EngineConfig生命监听器不是在server.xml配置的,而是在创建Engine是默认添加的。
3.2、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",
这段是为Engine创建集群配置,如需要,应在server.xml中江将Cluster标签注释打开,还未用过Tomcat的集群配置,相关内容就不详细介绍了。
3.3、为Engine添加生命周期监听器
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");
和EngineConfig不同,此处的生命周期监听器是在server.xml中配置的,默认未指定。
3.4、为Engine添加realm和valve
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");
realm和tomcat安全相关,valve同tomcat处理请求方式相关。
四、Host的解析
Host的解析,是在HostRuleSet类的addRuleInstances方法中。
4.1、创建Host实例
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);
创建默认为org.apache.catalina.core.StandardHost的实例,并将host标签上的属性设置到host实例中。接着调用Host实例中的setParentClassLoader将Engine中getParentClassLoader方法的类加载设置进来。
同Engine一样,在创建Host时,默认会添加一个生命周期监听器HostConfig,该生命周期监听器比EngineConfig复杂得多,和web应用的加载有关,将在下一篇介绍。
此外,通过Alias,Host支持别名配置。
4.2、Host集群配置
//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");
可见,集群配置可以在Engine级别,也可在Host级别。
4.3、为Host添加生命周期监听器
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");
此处的生命周期监听器是在server.xml中配置的,默认未指定。
4.4、为Host添加realm和valve
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");
从配置文件中可以看到默认的Valve为AccessLogValve,用于在处理请求时记录访问日志。
<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 "%r" %s %b"/>
</Host>
五、Context的解析
最后看下Context的解析,在ContextRuleSet的addRuleInstances方法中。
首先应该明白,Context配置并非来源一处,在Tomcat的conf目录下和Web应用的META-INF目录下都有Context.xml配置文件,而且在server.xml中也可以配置Context标签。
多数情况并不需要在server.xml中配置Context,更多是由HostConfig自动扫描部署目录,以Context.xml文件为基础进行Context的创建,具体将在下篇介绍。
而这里是指根据server.xml配置的Context进行解析。
5.1、Context实例化
if (create) {
digester.addObjectCreate(prefix + "Context",
"org.apache.catalina.core.StandardContext", "className");
digester.addSetProperties(prefix + "Context");
} else {
digester.addRule(prefix + "Context", new SetContextPropertiesRule());
}
if (create) {
digester.addRule(prefix + "Context",
new LifecycleListenerRule
("org.apache.catalina.startup.ContextConfig",
"configClass"));
digester.addSetNext(prefix + "Context",
"addChild",
"org.apache.catalina.Container");
}
Context的解析因create不同而不同。通过server.xml配置Context时,create为true,这时默认会创建org.apache.catalina.core.StandardContext的实例。
通过HostConfig自动创建Context时,create为false,此时仅解析子节点即可。
如果create为true,在创建Context同时,会为context实例添加一个生命周期监听器ContextConfig,用于详细配置Context,如解析web.xml等。
5.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");
默认未指定,可通过配置className属性指定。
5.3、为Context指定类加载器
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");
创建WebappLoader实例,并将其设置到Context实例中。
5.4、为Context指定会话管理器
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");
默认的会话管理器为org.apache.catalina.session.StandardManager,同时为管理器指定会话存储方式和会话标识生成器。
5.5、为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");
5.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");
5.7、为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");
具体Valve可由className指定。
5.8、为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");
WatchedResource标签可在Context.xml中找到,用于为Context添加监资源,当这些资源发生变化时,Web应用会被重新加载,默认为WEB-INF/web.xml。
WrapperListener标签用于为Context添加一个生命周期类,此类的实例不是添加到Context中,而是Context包含的Wrapper。
JarScanner标签为JarScanner指定一jar扫描器,JarScanFilter则为扫描器指定一个过滤器,只有符合条件的jar包才会被处理。
5.9、为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");
六、总结
上面只是简单介绍了Server的创建,并没有把每个组件都讲得很清楚,我没打算了解那么细。
该篇结束后,下篇将对Servlet容器的两个核心功能进行分析:部署Web应用和将请求映射到具体的Servlet进行处理。