tomcat--catalina


Servlet容器是tomcat的核心组件,所有基于jsp/servlet的Java web应用均需要依托servlet容器运行并对外提供服务。


tomcat本质上是一款servlet容器,因此Catalina是tomcat的核心,其它模块均为catalina提供支持。


Digester
Catalina使用digester解析xml配置文件,并创建应用服务器。

digester主要有对象栈、匹配模式和处理规则三部分。

对象栈保存各个节点对象。

digest用法:

解析用的代码:

public static void ts1() throws IOException, SAXException {
        Digester digester=new Digester();
        digester.setValidating(false);
        digester.setRulesValidation(true);
        digester.addObjectCreate("department",Department.class.getName());
        digester.addSetProperties("department");//匹配到department节点时,设置对象属性.比如name,age等自定义属性自动装填。

        digester.addObjectCreate("department/user",User.class.getName());
        digester.addSetProperties("department/user");
        digester.addSetNext("department/user","addUser",User.class.getName());

        digester.addCallMethod("department/extension","putExtension",2);
        digester.addCallParam("department/extension/property-name",0);
        digester.addCallParam("department/extension/property-value",1);

        Department d= (Department) digester.parse(TestMain.class.getResourceAsStream("/test.xml"));
        System.out.println(d.getCode());
    }

原文:

<?xml version="1.0" encoding="UTF-8" ?>
<department>
    <user name="un1" code="uc1"></user>
    <user name="un2" code="uc2"></user>
    <extension>
        <property-name>director</property-name>
        <property-value>joke</property-value>
    </extension>
</department>

Department和user是自定义的类,用于描述文件属性。


catalina类通过下面这个方法,创建了digester类。

protected Digester createStartDigester() {
        long t1 = System.currentTimeMillis();
        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);

        digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");//创建server对象,如果有自定义className,则使用自定义的属性
        digester.addSetProperties("Server");//装填属性
        digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");//设置到catalina类中

        digester.addObjectCreate("Server/GlobalNamingResources", "org.apache.catalina.deploy.NamingResourcesImpl");
        //创建Java EE企业命名上下文
        digester.addSetProperties("Server/GlobalNamingResources");
        digester.addSetNext("Server/GlobalNamingResources", "setGlobalNamingResources", "org.apache.catalina.deploy.NamingResourcesImpl");//将其设置到server实例中

        //为server创建声明周期监听器
        digester.addObjectCreate("Server/Listener", (String)null, "className");
        digester.addSetProperties("Server/Listener");
        digester.addSetNext("Server/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");
        //className指定LifecycleListener,Catalina默认配置了5个监听器。
        //Apr 在server初始化前加载APR库,并在server停止后销毁                 //VersionLogger  server初始化前打印操作系统,jvm以及服务器版本信息
        //JreMemoryLeakPreventionListener   server初始化前调用,以解决单例对象创建导致的jvm内存泄漏以及锁文件问题
        //GlobalResourcesLifecycleListener server启动时,将JNDI资源注册为MBean管理
        //ThreadLocalLeakPrevention 用于在context停止时重建Executor池中 的线程,避免导致内存泄漏

//构建service实例
        digester.addObjectCreate("Server/Service", "org.apache.catalina.core.StandardService", "className");
        digester.addSetProperties("Server/Service");
        digester.addSetNext("Server/Service", "addService", "org.apache.catalina.Service");//把实例添加到server中

        //为service添加生命周期监听器,默认情况下,Catalina未指定service监听器
        digester.addObjectCreate("Server/Service/Listener", (String)null, "className");
        digester.addSetProperties("Server/Service/Listener");
        digester.addSetNext("Server/Service/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");

    //为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");

    //为service添加connector,排除executor和sslImplementationName
        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添加虚拟主机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");

//为connector添加生命周期监听器
        digester.addObjectCreate("Server/Service/Connector/Listener", (String)null, "className");
        digester.addSetProperties("Server/Service/Connector/Listener");
        digester.addSetNext("Server/Service/Connector/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");

//为connector添加升级协议,用于支持HTTP/2
        digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol", (String)null, "className");
        digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");
        digester.addSetNext("Server/Service/Connector/UpgradeProtocol", "addUpgradeProtocol", "org.apache.coyote.UpgradeProtocol");

//添加子元素解析规则
        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/"));
        this.addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
        digester.addRule("Server/Service/Engine", new SetParentClassLoaderRule(this.parentClassLoader));
        this.addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");
        long t2 = System.currentTimeMillis();
        if(log.isDebugEnabled()) {
            log.debug("Digester for server.xml created " + (t2 - t1));
        }

        return digester;
    }

Engine的解析

默认实现是StandardEngine,提供了默认配置EngineConfig用于打印engine的启动和停止日志。

public void addRuleInstances(Digester digester) {
        digester.addObjectCreate(this.prefix + "Engine", "org.apache.catalina.core.StandardEngine", "className");
        digester.addSetProperties(this.prefix + "Engine");

        digester.addRule(this.prefix + "Engine", new LifecycleListenerRule("org.apache.catalina.startup.EngineConfig", "engineConfigClass"));
        digester.addSetNext(this.prefix + "Engine", "setContainer", "org.apache.catalina.Engine");//添加到service实例中
        //添加集群配置
        digester.addObjectCreate(this.prefix + "Engine/Cluster", (String)null, "className");
        digester.addSetProperties(this.prefix + "Engine/Cluster");
        digester.addSetNext(this.prefix + "Engine/Cluster", "setCluster", "org.apache.catalina.Cluster");
        //添加生命周期监听器
        digester.addObjectCreate(this.prefix + "Engine/Listener", (String)null, "className");
        digester.addSetProperties(this.prefix + "Engine/Listener");
        digester.addSetNext(this.prefix + "Engine/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");
        //添加安全配置
        digester.addRuleSet(new RealmRuleSet(this.prefix + "Engine/"));
        digester.addObjectCreate(this.prefix + "Engine/Valve", (String)null, "className");
        digester.addSetProperties(this.prefix + "Engine/Valve");
        digester.addSetNext(this.prefix + "Engine/Valve", "addValve", "org.apache.catalina.Valve");
    }

host解析

public void addRuleInstances(Digester digester) {
        digester.addObjectCreate(this.prefix + "Host", "org.apache.catalina.core.StandardHost", "className");
        digester.addSetProperties(this.prefix + "Host");//注入属性
        digester.addRule(this.prefix + "Host", new CopyParentClassLoaderRule());
        digester.addRule(this.prefix + "Host", new LifecycleListenerRule("org.apache.catalina.startup.HostConfig", "hostConfigClass"));
        //添加监听器
        digester.addSetNext(this.prefix + "Host", "addChild", "org.apache.catalina.Container");
    //把它添加到engine中

        digester.addCallMethod(this.prefix + "Host/Alias", "addAlias", 0);//添加别名

        //添加集群,为Host
        digester.addObjectCreate(this.prefix + "Host/Cluster", (String)null, "className");
        digester.addSetProperties(this.prefix + "Host/Cluster");
        digester.addSetNext(this.prefix + "Host/Cluster", "setCluster", "org.apache.catalina.Cluster");
        //添加生命周期管理
        digester.addObjectCreate(this.prefix + "Host/Listener", (String)null, "className");
        digester.addSetProperties(this.prefix + "Host/Listener");
        digester.addSetNext(this.prefix + "Host/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");
        //添加安全管理
        digester.addRuleSet(new RealmRuleSet(this.prefix + "Host/"));
        digester.addObjectCreate(this.prefix + "Host/Valve", (String)null, "className");
        digester.addSetProperties(this.prefix + "Host/Valve");
        digester.addSetNext(this.prefix + "Host/Valve", "addValve", "org.apache.catalina.Valve");
    }

context解析

create为true时,表示通过server.xml配置context。
create为false时,通过HostConfig创建context。

public void addRuleInstances(Digester digester) {
        if(this.create) {
            digester.addObjectCreate(this.prefix + "Context", "org.apache.catalina.core.StandardContext", "className");
            digester.addSetProperties(this.prefix + "Context");
        } else {
            digester.addRule(this.prefix + "Context", new SetContextPropertiesRule());
        }

        if(this.create) {
            digester.addRule(this.prefix + "Context", new LifecycleListenerRule("org.apache.catalina.startup.ContextConfig", "configClass"));//生命周期监听器,用于详细配置context,如详细解析web.xml
            digester.addSetNext(this.prefix + "Context", "addChild", "org.apache.catalina.Container");
        }

        digester.addObjectCreate(this.prefix + "Context/Listener", (String)null, "className");
        digester.addSetProperties(this.prefix + "Context/Listener");
        digester.addSetNext(this.prefix + "Context/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");
//指定生命周期监听器

        digester.addObjectCreate(this.prefix + "Context/Loader", "org.apache.catalina.loader.WebappLoader", "className");
        digester.addSetProperties(this.prefix + "Context/Loader");
        digester.addSetNext(this.prefix + "Context/Loader", "setLoader", "org.apache.catalina.Loader");
//指定类加载器

        digester.addObjectCreate(this.prefix + "Context/Manager", "org.apache.catalina.session.StandardManager", "className");
        digester.addSetProperties(this.prefix + "Context/Manager");
        digester.addSetNext(this.prefix + "Context/Manager", "setManager", "org.apache.catalina.Manager");

        digester.addObjectCreate(this.prefix + "Context/Manager/Store", (String)null, "className");
        digester.addSetProperties(this.prefix + "Context/Manager/Store");
        digester.addSetNext(this.prefix + "Context/Manager/Store", "setStore", "org.apache.catalina.Store");
        digester.addObjectCreate(this.prefix + "Context/Manager/SessionIdGenerator", "org.apache.catalina.util.StandardSessionIdGenerator", "className");
        digester.addSetProperties(this.prefix + "Context/Manager/SessionIdGenerator");
        digester.addSetNext(this.prefix + "Context/Manager/SessionIdGenerator", "setSessionIdGenerator", "org.apache.catalina.SessionIdGenerator");
//会话管理器

        digester.addObjectCreate(this.prefix + "Context/Parameter", "org.apache.tomcat.util.descriptor.web.ApplicationParameter");
        digester.addSetProperties(this.prefix + "Context/Parameter");
        digester.addSetNext(this.prefix + "Context/Parameter", "addApplicationParameter", "org.apache.tomcat.util.descriptor.web.ApplicationParameter");
//添加初始化参数,这个会导致应用与tomcat紧耦合(一般很少更换服务容器,所以这个其实没什么影响)。

        digester.addRuleSet(new RealmRuleSet(this.prefix + "Context/"));
        digester.addObjectCreate(this.prefix + "Context/Resources", "org.apache.catalina.webresources.StandardRoot", "className");
        digester.addSetProperties(this.prefix + "Context/Resources");
        digester.addSetNext(this.prefix + "Context/Resources", "setResources", "org.apache.catalina.WebResourceRoot");
        digester.addObjectCreate(this.prefix + "Context/Resources/PreResources", (String)null, "className");
        digester.addSetProperties(this.prefix + "Context/Resources/PreResources");
        digester.addSetNext(this.prefix + "Context/Resources/PreResources", "addPreResources", "org.apache.catalina.WebResourceSet");
        digester.addObjectCreate(this.prefix + "Context/Resources/JarResources", (String)null, "className");
        digester.addSetProperties(this.prefix + "Context/Resources/JarResources");
        digester.addSetNext(this.prefix + "Context/Resources/JarResources", "addJarResources", "org.apache.catalina.WebResourceSet");
        digester.addObjectCreate(this.prefix + "Context/Resources/PostResources", (String)null, "className");
        digester.addSetProperties(this.prefix + "Context/Resources/PostResources");
        digester.addSetNext(this.prefix + "Context/Resources/PostResources", "addPostResources", "org.apache.catalina.WebResourceSet");
//添加安全配置以及资源配置

        digester.addObjectCreate(this.prefix + "Context/ResourceLink", "org.apache.tomcat.util.descriptor.web.ContextResourceLink");
        digester.addSetProperties(this.prefix + "Context/ResourceLink");
        digester.addRule(this.prefix + "Context/ResourceLink", new SetNextNamingRule("addResourceLink", "org.apache.tomcat.util.descriptor.web.ContextResourceLink"));
     //添加资源链接,用于Java EE   命名服务

        digester.addObjectCreate(this.prefix + "Context/Valve", (String)null, "className");
        digester.addSetProperties(this.prefix + "Context/Valve");
        digester.addSetNext(this.prefix + "Context/Valve", "addValve", "org.apache.catalina.Valve");
//添加拦截器Value

        digester.addCallMethod(this.prefix + "Context/WatchedResource", "addWatchedResource", 0);//添加监视资源,这些资源发生变化时,web应用会重新加载,默认是web.xml
        digester.addCallMethod(this.prefix + "Context/WrapperLifecycle", "addWrapperLifecycle", 0);//为context添加生命周期监听器,添加到context包含的wrapper上
        digester.addCallMethod(this.prefix + "Context/WrapperListener", "addWrapperListener", 0);
        digester.addObjectCreate(this.prefix + "Context/JarScanner", "org.apache.tomcat.util.scan.StandardJarScanner", "className");
        digester.addSetProperties(this.prefix + "Context/JarScanner");
        digester.addSetNext(this.prefix + "Context/JarScanner", "setJarScanner", "org.apache.tomcat.JarScanner");
        digester.addObjectCreate(this.prefix + "Context/JarScanner/JarScanFilter", "org.apache.tomcat.util.scan.StandardJarScanFilter", "className");
        digester.addSetProperties(this.prefix + "Context/JarScanner/JarScanFilter");
        digester.addSetNext(this.prefix + "Context/JarScanner/JarScanFilter", "setJarScanFilter", "org.apache.tomcat.JarScanFilter");
//添加守护资源配置

        digester.addObjectCreate(this.prefix + "Context/CookieProcessor", "org.apache.tomcat.util.http.Rfc6265CookieProcessor", "className");
        digester.addSetProperties(this.prefix + "Context/CookieProcessor");
        digester.addSetNext(this.prefix + "Context/CookieProcessor", "setCookieProcessor", "org.apache.tomcat.util.http.CookieProcessor");
    }
    //添加cookie管理器

web应用加载是server启动的核心处理过程,Catalina通过Standard host、hostconfig,standardcontext、contextConfig、standardWrapper这5个类完成对web应用的加载。


StandardHost加载web应用(即standardContext)的入口有2个,其中一个入口是由生命周期管理接口调用start方法启动,这是配置context元素在host元素内(解析server.xml时会创建context),这种方式不好用,因为每部署一个web应用,都需要在这来配置。

另一个入口是HostConfig自动扫描部署目录,创建context实例并启动。

StandardHost启动加载过程:

  1. 为host添加一个Value实现ErrorReportValue(也可以修改host的errorReportValueClass属性指定自己的错误处理Value),用于在服务器处理异常时输出错误页面。如果web.xml中没有添加错误处理页面,tomcat就会返回这个类生成的页面。
  2. 调用StandardHost父类ContainerBase的startInternal方法启动虚拟主机,处理分为以下几步
    1. 如果配置了集群组件Cluster,则启动
    2. 如果配置了安全组件Realm,则启动
    3. 启动子节点
    4. 启动Host持有的Pipeline组件
    5. 设置Host状态为Starting,此时会触发StartEvent事件,HostConfig监听这个事件,扫描Web部署目录,对于部署描述文件、WAR包等,会自动创建StandardContext实例,并添加到Context中。
    6. 启动Host层级的后台任务处理:Cluster后台任务、Realm后台任务处理、Pipeline中Value的后台任务处理。

HostConfig---- 它是一个LifecycleListener实现,由catalina添加到Host实例上。处理的生命周期事件包括Start_EVENT,PERIODIC_EVENT,STOP_EVENT。前两者与web应用部署密切相关,后者用于在Host停止时注销其对应的MBean。

START_EVENT事件
该事件在host启动时触发,完成服务器启动过程中的web应用部署(需要deployOnStartup为true,默认是true)。
· context描述文件部署:tomcat支持通过一个独立的Context描述文件来配置并启动web应用,配置方式如同server.xml中的context元素。配置文件的存储路径由host的xmlBase属性指定,默认为$CATALINA_BASE/conf/< Engine名称 >/host名称。
这个就会加载test/myApp目录下的应用,访问路径则path。

context的描述文件由hostConfig进行扫描和解析,而hostconfig会使用线程池同时解析多个context的xml文件。

而每个线程使用以下代码解析单个xml文件:

protected void deployDescriptor(ContextName cn, File contextXml) {
        HostConfig.DeployedApplication deployedApp = new HostConfig.DeployedApplication(cn.getName(), true);
        long startTime = 0L;
        if(log.isInfoEnabled()) {
            startTime = System.currentTimeMillis();
            log.info(sm.getString("hostConfig.deployDescriptor", new Object[]{contextXml.getAbsolutePath()}));
        }

        Context context = null;
        boolean isExternalWar = false;
        boolean isExternal = false;
        File expandedDocBase = null;
        boolean var30 = false;

        label870: {
            boolean unpackWAR;
            File warDocBase;
            label871: {
                try {
                    var30 = true;
                    FileInputStream fis = new FileInputStream(contextXml);
                    Throwable var11 = null;

                    try {
                        Object var12 = this.digesterLock;
                        synchronized(this.digesterLock) {
                            try {
                                context = (Context)this.digester.parse(fis);
                            } catch (Exception var49) {
                                log.error(sm.getString("hostConfig.deployDescriptor.error", new Object[]{contextXml.getAbsolutePath()}), var49);
                            } finally {
                                this.digester.reset();
                                if(context == null) {
                                    context = new FailedContext();
                                }

                            }
                        }

                        Class<?> clazz = Class.forName(this.host.getConfigClass());
                        LifecycleListener listener = (LifecycleListener)clazz.newInstance();
                        ((Context)context).addLifecycleListener(listener);
                        ((Context)context).setConfigFile(contextXml.toURI().toURL());
                        ((Context)context).setName(cn.getName());
                        ((Context)context).setPath(cn.getPath());
                        ((Context)context).setWebappVersion(cn.getVersion());
                        if(((Context)context).getDocBase() != null) {
                            File docBase = new File(((Context)context).getDocBase());
                            if(!docBase.isAbsolute()) {
                                docBase = new File(this.host.getAppBaseFile(), ((Context)context).getDocBase());
                            }

                            if(!docBase.getCanonicalPath().startsWith(this.host.getAppBaseFile().getAbsolutePath() + File.separator)) {
                                isExternal = true;
                                deployedApp.redeployResources.put(contextXml.getAbsolutePath(), Long.valueOf(contextXml.lastModified()));
                                deployedApp.redeployResources.put(docBase.getAbsolutePath(), Long.valueOf(docBase.lastModified()));
                                if(docBase.getAbsolutePath().toLowerCase(Locale.ENGLISH).endsWith(".war")) {
                                    isExternalWar = true;
                                }
                            } else {
                                log.warn(sm.getString("hostConfig.deployDescriptor.localDocBaseSpecified", new Object[]{docBase}));
                                ((Context)context).setDocBase((String)null);
                            }
                        }

                        this.host.addChild((Container)context);
                    } catch (Throwable var52) {
                        var11 = var52;
                        throw var52;
                    } finally {
                        if(fis != null) {
                            if(var11 != null) {
                                try {
                                    fis.close();
                                } catch (Throwable var48) {
                                    var11.addSuppressed(var48);
                                }
                            } else {
                                fis.close();
                            }
                        }

                    }

                    var30 = false;
                    break label871;
                } catch (Throwable var54) {
                    ExceptionUtils.handleThrowable(var54);
                    log.error(sm.getString("hostConfig.deployDescriptor.error", new Object[]{contextXml.getAbsolutePath()}), var54);
                    var30 = false;
                } finally {
                    if(var30) {
                        expandedDocBase = new File(this.host.getAppBaseFile(), cn.getBaseName());
                        if(((Context)context).getDocBase() != null && !((Context)context).getDocBase().toLowerCase(Locale.ENGLISH).endsWith(".war")) {
                            expandedDocBase = new File(((Context)context).getDocBase());
                            if(!expandedDocBase.isAbsolute()) {
                                expandedDocBase = new File(this.host.getAppBaseFile(), ((Context)context).getDocBase());
                            }
                        }

                        boolean unpackWAR = this.unpackWARs;
                        if(unpackWAR && context instanceof StandardContext) {
                            unpackWAR = ((StandardContext)context).getUnpackWAR();
                        }

                        if(isExternalWar) {
                            if(unpackWAR) {
                                deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(), Long.valueOf(expandedDocBase.lastModified()));
                                this.addWatchedResources(deployedApp, expandedDocBase.getAbsolutePath(), (Context)context);
                            } else {
                                this.addWatchedResources(deployedApp, (String)null, (Context)context);
                            }
                        } else {
                            if(!isExternal) {
                                File warDocBase = new File(expandedDocBase.getAbsolutePath() + ".war");
                                if(warDocBase.exists()) {
                                    deployedApp.redeployResources.put(warDocBase.getAbsolutePath(), Long.valueOf(warDocBase.lastModified()));
                                } else {
                                    deployedApp.redeployResources.put(warDocBase.getAbsolutePath(), Long.valueOf(0L));
                                }
                            }

                            if(unpackWAR) {
                                deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(), Long.valueOf(expandedDocBase.lastModified()));
                                this.addWatchedResources(deployedApp, expandedDocBase.getAbsolutePath(), (Context)context);
                            } else {
                                this.addWatchedResources(deployedApp, (String)null, (Context)context);
                            }

                            if(!isExternal) {
                                deployedApp.redeployResources.put(contextXml.getAbsolutePath(), Long.valueOf(contextXml.lastModified()));
                            }
                        }

                        this.addGlobalRedeployResources(deployedApp);
                    }
                }

                expandedDocBase = new File(this.host.getAppBaseFile(), cn.getBaseName());
                if(((Context)context).getDocBase() != null && !((Context)context).getDocBase().toLowerCase(Locale.ENGLISH).endsWith(".war")) {
                    expandedDocBase = new File(((Context)context).getDocBase());
                    if(!expandedDocBase.isAbsolute()) {
                        expandedDocBase = new File(this.host.getAppBaseFile(), ((Context)context).getDocBase());
                    }
                }

                unpackWAR = this.unpackWARs;
                if(unpackWAR && context instanceof StandardContext) {
                    unpackWAR = ((StandardContext)context).getUnpackWAR();
                }

                if(isExternalWar) {
                    if(unpackWAR) {
                        deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(), Long.valueOf(expandedDocBase.lastModified()));
                        this.addWatchedResources(deployedApp, expandedDocBase.getAbsolutePath(), (Context)context);
                    } else {
                        this.addWatchedResources(deployedApp, (String)null, (Context)context);
                    }
                } else {
                    if(!isExternal) {
                        warDocBase = new File(expandedDocBase.getAbsolutePath() + ".war");
                        if(warDocBase.exists()) {
                            deployedApp.redeployResources.put(warDocBase.getAbsolutePath(), Long.valueOf(warDocBase.lastModified()));
                        } else {
                            deployedApp.redeployResources.put(warDocBase.getAbsolutePath(), Long.valueOf(0L));
                        }
                    }

                    if(unpackWAR) {
                        deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(), Long.valueOf(expandedDocBase.lastModified()));
                        this.addWatchedResources(deployedApp, expandedDocBase.getAbsolutePath(), (Context)context);
                    } else {
                        this.addWatchedResources(deployedApp, (String)null, (Context)context);
                    }

                    if(!isExternal) {
                        deployedApp.redeployResources.put(contextXml.getAbsolutePath(), Long.valueOf(contextXml.lastModified()));
                    }
                }

                this.addGlobalRedeployResources(deployedApp);
                break label870;
            }

            expandedDocBase = new File(this.host.getAppBaseFile(), cn.getBaseName());
            if(((Context)context).getDocBase() != null && !((Context)context).getDocBase().toLowerCase(Locale.ENGLISH).endsWith(".war")) {
                expandedDocBase = new File(((Context)context).getDocBase());
                if(!expandedDocBase.isAbsolute()) {
                    expandedDocBase = new File(this.host.getAppBaseFile(), ((Context)context).getDocBase());
                }
            }

            unpackWAR = this.unpackWARs;
            if(unpackWAR && context instanceof StandardContext) {
                unpackWAR = ((StandardContext)context).getUnpackWAR();
            }

            if(isExternalWar) {
                if(unpackWAR) {
                    deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(), Long.valueOf(expandedDocBase.lastModified()));
                    this.addWatchedResources(deployedApp, expandedDocBase.getAbsolutePath(), (Context)context);
                } else {
                    this.addWatchedResources(deployedApp, (String)null, (Context)context);
                }
            } else {
                if(!isExternal) {
                    warDocBase = new File(expandedDocBase.getAbsolutePath() + ".war");
                    if(warDocBase.exists()) {
                        deployedApp.redeployResources.put(warDocBase.getAbsolutePath(), Long.valueOf(warDocBase.lastModified()));
                    } else {
                        deployedApp.redeployResources.put(warDocBase.getAbsolutePath(), Long.valueOf(0L));
                    }
                }

                if(unpackWAR) {
                    deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(), Long.valueOf(expandedDocBase.lastModified()));
                    this.addWatchedResources(deployedApp, expandedDocBase.getAbsolutePath(), (Context)context);
                } else {
                    this.addWatchedResources(deployedApp, (String)null, (Context)context);
                }

                if(!isExternal) {
                    deployedApp.redeployResources.put(contextXml.getAbsolutePath(), Long.valueOf(contextXml.lastModified()));
                }
            }

            this.addGlobalRedeployResources(deployedApp);
        }

        if(this.host.findChild(((Context)context).getName()) != null) {
            this.deployed.put(((Context)context).getName(), deployedApp);
        }

        if(log.isInfoEnabled()) {
            log.info(sm.getString("hostConfig.deployDescriptor.finished", new Object[]{contextXml.getAbsolutePath(), Long.valueOf(System.currentTimeMillis() - startTime)}));
        }

    }

ServletContext实现类为ApplicationContext。ApplicationFilterConfig负责Filter的实例化。FilterMap存储filter- mapping的配置。NamingResource存储web应用 的命名服务JNDI。

StandardContext的启动过程:

  1. 发布正在启动的JMX通知,这样可以添加NotificationList来监听web应用的启动
  2. 启动当前Context维护的JNDI资源
  3. 初始化当前Context使用的WebResourceRoot并启动,WebResourceRoot维护了所有的资源集合(class文件、jar包以及其它资源文件),主要用于类加载和按照路径查找资源。
  4. 创建web应用类加载器
  5. 如果当前Context使用JNDI,则为其添加NamingContextListener
  6. 启动web应用类加载器
  7. 启用安全组件Realm
  8. 发布Configure_Start_EVENT事件
  9. 启动context子节点
  10. 启用context维护的pipeline
  11. 创建会话管理器
  12. 将context的web资源集合添加到servletContext属性,属性名为org.apache.catalina.resources。
  13. 启动添加到当前Context的ServletContaionerInitializer
  14. 实例化应用监听器
  15. 检测未覆盖的HTTP方法的安全约束
  16. 启动会话管理器
  17. 实例化FilterConfig,Filter,并调用Filter的init初始化
  18. 启动后台定时任务
  19. 发布正在运行的JMX通知
  20. 设置context状态

ContextConfig是Context的生命周期监听器

context的创建途径:

  1. server.xml中有配置这个元素(这种方式一般不用,麻烦)
  2. HostConfig部署web应用时,解析web应用中的META-INF/context.xml创建(没有这个目录或文件也没关系,直接自动创建)
  3. Host部署Web应用时,解析$CATALINA_BASE/conf/Engine名/Host名 目录下的context文件创建(这种方式是为设置一些第二种方式设置不了的属性用的)

context的默认配置,优先使用conf目录下的,再次是conf/eng/host/目录下的,最后是context的configfile属性。

Before_start_event事件

在context启动之前触发,用于更新context的docBase属性和解决web目录锁的问题。
更新docBase是针对本身为war包的情况,修改docbase为解压后的文件目录
具体处理过程:

  1. 根据Host的appBase以及context的docbase计算docbase的绝对路径
  2. 如果docbase是war,则先解压,然后更新docbase为解压后的位置
  3. 如果docbase是一个不存在的目录,但是存在同名war包,则解压部署、
  4. 如果docbase是一个有效目录,当时存在同名war,如果允许覆盖,则重新解压

当antiResourceLocking属性为true时,tomcat会把web包或目录复制到临时文件目录。将context的docbase更新为临时目录下的web应用。

standardWrapper的start方法广播通知了它的状态变化,然后加载了所有启动时加载的servlet。

Tomcat通过Mapper维护请求链接与Host,context,wrapper等与contaioner的映射。同时通过MapperListener监听器监听所有的Host,context,wrapper组件,在相关组件启动、停止时注册或移除相关映射。

Connector接收到请求后,首先读取请求数据,然后调用CoyoteAdapter.service()完成请求处理。

根据connector的请求和响应对象创建Servlet请求,转换请求参数并完成映射,
请求URI解码,初始化请求的路径参数,检查URI是否合法,请求映射


原文链接:https://blog.csdn.net/ljz2016/article/details/84579671