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 &quot;%r&quot; %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进行处理。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容