Tomcat中的层次

Catalina -> Server --- 
                      |---globalNamingResources
                      |---namingContextListener
                      |---多个Service --------------------|
                      |---catalina.home                  |---Engine(每个Service最多一个) ---多个Container(比如host)
                      |---catalina.base

Tomcat有明显的层次关系上层只会对自己直接下一层的组件负责初始化,然后下一组件再对自己的子组件进行初始化.

可以看到,Tomcat中如果部署多个应用时,每一个Host,就代表了一主机.可以这样了比作:

Catalina或Server就是一个服务器.

Host就是该服务器上的一个虚拟主机.

Application就是虚拟主机上的一个应用.(webapss目录下的每一个目录就对应一个application,在Tomcat里,用 org.apache.catalina.startup.HostConfig 类的内部类 DeployedApplication 来表示.)

初始化一个 Host

每一个Host,都有一个org.apache.catalina.startup.HostConfig对象代表.它会根据配置文件conf/server.xml中的<host>节点相匹配.

部署的代码为:

    protected void deployApps() {

        File appBase = host.getAppBaseFile();
        File configBase = host.getConfigBaseFile();
        String[] filteredAppPaths = filterAppPaths(appBase.list());
        // Deploy XML descriptors from configBase
        deployDescriptors(configBase, configBase.list());
        // Deploy WARs
        deployWARs(appBase, filteredAppPaths);
        // Deploy expanded folders
        deployDirectories(appBase, filteredAppPaths);

    }

部署 deployDescriptors

先部署deployDescriptors(),它的描述文件目录在conf/Catalina/localhost这个虚拟主机目录下.比如,创建一个文件名为myapp.xml,内容为:

<Context path="/myapp" docBase="/home/sky/ROOT" debug="0" privileged="true"></Context>

它就会根据该目录下的myapp.xml文件配置来部署应用.比如上面的例子,它会部署一个path/myapp, 部署的目录为/home/sky/ROOT.这样子,就可以通过http://localhost:8080/myapp来访问了. 这种方式,可以不必将应用程序的目录,放到webapps目录下.

部署 wars

它会根据在配置文件conf/server.xml中的<host>appBase属性的值所在的目录(默认为webapps)下查找.war结尾的文件, 然后再判断是否需要解压(根据配置文件中的 unpackWARs 属性).

如果解压了,就和部署目录的逻辑是一样的.

部署 目录

它的代码如下,可以看到,对于每个目录,它是通过多线程来进行部署的.每一个目录都有一条线程去负责部署.而且是利用Host的getStartStopExecutor来执行的.

    private static class DeployDirectory implements Runnable {

        private HostConfig config;
        private ContextName cn;
        private File dir;

        public DeployDirectory(HostConfig config, ContextName cn, File dir) {
            this.config = config;
            this.cn = cn;
            this.dir = dir;
        }

        @Override
        public void run() {
            config.deployDirectory(cn, dir);
        }
    }

部署的主要逻辑,是初始化一个StandardContext对象(还带有一个 ContextConfig 对象, 这个config对象,就是web.xml的代表. ).代表该应用的上下文对象.然后将host的listener(相当于host对所有应用都生效的listener,即对host来说是全局应用到每个应用的)添加到该application的Listener生命周期中.代码逻辑如下:

Class<?> clazz = Class.forName(host.getConfigClass());
LifecycleListener listener =  (LifecycleListener) clazz.newInstance(); # 这里就添加了一个 ContextConfig 对象.它随着 StandardContext的生命周期一起存灭.

context.addLifecycleListener(listener);
context.setName(cn.getName());
context.setPath(cn.getPath());
context.setWebappVersion(cn.getVersion());
context.setDocBase(cn.getBaseName());
host.addChild(context);

cn为ContextName,它的构造逻辑如下:

根据webapps目录下的目录名,创建相应的应用名(里面有个特殊判断,如果目录名为ROOT,这个名字是硬编码写死的.那么它的path为”/“,即不需要通过应用名即可访问.)

ContextConfig 类

它会解析 StandardContext 以及 Webxml , 逻辑在 init 方法中:

 protected void init() {
        // Called from StandardContext.init()

        Digester contextDigester = createContextDigester(); 
        contextDigester.getParser();

        if (log.isDebugEnabled()) {
            log.debug(sm.getString("contextConfig.init"));
        }
        context.setConfigured(false);
        ok = true;

        contextConfig(contextDigester);

        webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
                context.getXmlValidation(), context.getXmlBlockExternal()); # 解析web.xml
    }

它和解析Tomcat的conf/server.xml这些类似,都是初始化相应的组件.

web.xml所有组件,都在org.apache.tomcat.util.descriptor.web.WebRuleSet里可以看到.(这里只列出部分,以免占太多篇幅)


        digester.addCallMethod(fullPrefix + "/servlet/servlet-class",
                              "setServletClass", 0);
        digester.addCallMethod(fullPrefix + "/servlet/servlet-name",
                              "setServletName", 0);

        digester.addObjectCreate(fullPrefix + "/servlet/multipart-config",
                                 "org.apache.tomcat.util.descriptor.web.MultipartDef");
        digester.addSetNext(fullPrefix + "/servlet/multipart-config",
                            "setMultipartDef",
                            "org.apache.tomcat.util.descriptor.web.MultipartDef");
        digester.addCallMethod(fullPrefix + "/servlet/multipart-config/location",
                               "setLocation", 0);

比如Filter,Listener, Session参数等等.

web.xml 的对应类 WebXml

WebXml类是web.xml的对应代表类.

各个 web.xml 版本对应的J2EE版本.

org.apache.tomcat.util.descriptor.DigesterFactory 在这人类有明确的对照表.

参考资料

CSDN aesop_wubo