context创建过程解析(一)之deployDescriptors

总结:主要是创建Context对象,并且将默认context配置,host级别配置,context配置的值设置进去,设置docBase,如果是war包就解压到webapp的目录中,重新设置docBase为war包解压后的目录。如果配置文件中没有指定docBase,那么就以webapps为基路径+context的baseName作为docBase

HostConfig.deployApps()
//在监听到start事件类型,也就是StandardHost调用startInternal
protected void deployApps() {

        File appBase = host.getAppBaseFile();
//这个值是在触发before_start时间时生成的,默认是tomcat安装目录+engine名+host名
        File configBase = host.getConfigBaseFile();
//获取host上配置的webapp下的所有文件,默认是webapps目录下的所有文件
        String[] filteredAppPaths = filterAppPaths(appBase.list());
        // Deploy XML descriptors from configBase  发布xml描述文件
        deployDescriptors(configBase, configBase.list());
        // Deploy WARs
        deployWARs(appBase, filteredAppPaths);
        // Deploy expanded folders
        deployDirectories(appBase, filteredAppPaths);

    }

deployDescriptors

protected void deployDescriptors(File configBase, String[] files) {

        if (files == null)
            return;

        ExecutorService es = host.getStartStopExecutor();//获取线程池
        List<Future<?>> results = new ArrayList<>();

        for (int i = 0; i < files.length; i++) {
            File contextXml = new File(configBase, files[i]);

            if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".xml")) {
//context命名,在构造函数里面进行设置,设置版本,path,命名。
                ContextName cn = new ContextName(files[i], true);
//是否已经部署过,如果已经部署过了,就不再进行部署
                if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
                    continue;
//异步发布context描述xml
                results.add(
                        es.submit(new DeployDescriptor(this, cn, contextXml)));
            }
        }

        for (Future<?> result : results) {
            try {
//等待异步部署context描述文件结束
                result.get();
            } catch (Exception e) {
                log.error(sm.getString(
                        "hostConfig.deployDescriptor.threaded.error"), e);
            }
        }
    }

ContextName.ContextName()

//name 文件名
public ContextName(String name, boolean stripFileExtension) {
//假设文件名为myContext.xml和my/context.xml
        String tmp1 = name;

        // Convert Context names and display names to base names

        // Strip off any leading "/"  剥离开头的/
        if (tmp1.startsWith("/")) {
            tmp1 = tmp1.substring(1);
        }

        // Replace any remaining / 将/替换成#,如果是myContext.xml,那么依然是myContext.xml,如果是my/context.xml,那么就是my#context.xml
        tmp1 = tmp1.replaceAll("/", FWD_SLASH_REPLACEMENT);

        // Insert the ROOT name if required  如果需要,插入ROOT名称
//如果是以##开头或者没有名字的
        if (tmp1.startsWith(VERSION_MARKER) || "".equals(tmp1)) {
            tmp1 = ROOT_NAME + tmp1;//比如##a,就会变成ROOT##a,为空就是ROOT
        }

        // Remove any file extensions 去除扩展名
        if (stripFileExtension &&
                (tmp1.toLowerCase(Locale.ENGLISH).endsWith(".war") ||
                        tmp1.toLowerCase(Locale.ENGLISH).endsWith(".xml"))) {
            tmp1 = tmp1.substring(0, tmp1.length() -4);
        }

 baseName = tmp1;//myContext,my#context

        String tmp2;
        // Extract version number  提取版本号
        int versionIndex = baseName.indexOf(VERSION_MARKER);
        if (versionIndex > -1) {
//如果存在##这种的,提取##后面作为版本号
            version = baseName.substring(versionIndex + 2);
//比如myContext##1.2,这里version就是1.2,tmp2为myContext
            tmp2 = baseName.substring(0, versionIndex);
        } else {
            version = "";
            tmp2 = baseName;
        }
//如果为ROOT,那么path路径为域名根目录
        if (ROOT_NAME.equals(tmp2)) {
            path = "";
        } else {
//   /myContext   /my/context
            path = "/" + tmp2.replaceAll(FWD_SLASH_REPLACEMENT, "/");
        }
//如果存在版本号的应用
        if (versionIndex > -1) {
// /myContext##1.2   /my/context##1.2
            this.name = path + VERSION_MARKER + version;
        } else {
            this.name = path;
        }
    }

DeployDescriptor.run()

public void run() {
            config.deployDescriptor(cn, descriptor);
 }

HostConfig.deployDescriptor(ContextName cn, File contextXml)

protected void deployDescriptor(ContextName cn, File contextXml) {
//发布应用,用于记录发布的context名,和是否有描述文件,一些可能被修改的文件的修改时间,用于日后检测是否需要重新加载
        DeployedApplication deployedApp =
                new DeployedApplication(cn.getName(), true);

        long startTime = 0;
        // Assume this is a configuration descriptor and deploy it
        if(log.isInfoEnabled()) {
           startTime = System.currentTimeMillis();
           log.info(sm.getString("hostConfig.deployDescriptor",
                    contextXml.getAbsolutePath()));
        }

        Context context = null;
//是否为扩展war包
        boolean isExternalWar = false;
//是否是扩展web 应用
        boolean isExternal = false;
//记录扩展web应用的地址
        File expandedDocBase = null;

        try (FileInputStream fis = new FileInputStream(contextXml)) {
            synchronized (digesterLock) {
                try {
//解析contextxml文件
                    context = (Context) digester.parse(fis);
                } catch (Exception e) {
                    log.error(sm.getString(
                            "hostConfig.deployDescriptor.error",
                            contextXml.getAbsolutePath()), e);
                } finally {
//释放内存
                    digester.reset();
//如果创建失败,就new出一个失败的context
                    if (context == null) {
                        context = new FailedContext();
                    }
                }
            }
//host.getConfigClass() == org.apache.catalina.startup.ContextConfig
            Class<?> clazz = Class.forName(host.getConfigClass());
            LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
            //给context设置ContextConfig生命周期监听器
context.addLifecycleListener(listener);

            context.setConfigFile(contextXml.toURI().toURL());
            context.setName(cn.getName());
            context.setPath(cn.getPath());//一般我们在config/engine名+host名下的配置文件都是
            context.setWebappVersion(cn.getVersion());
            // Add the associated docBase to the redeployed list if it‘s a WAR
            if (context.getDocBase() != null) {
                File docBase = new File(context.getDocBase());
                if (!docBase.isAbsolute()) {
                    docBase = new File(host.getAppBaseFile(), context.getDocBase());
                }
                // If external docBase, register .xml as redeploy first,如果是扩展的web应用
//那么首先进行重发布处理
                if (!docBase.getCanonicalPath().startsWith(
                        host.getAppBaseFile().getAbsolutePath() + File.separator)) {
                    isExternal = true;
//设置应用配置xml为重发布资源,记录最后修改的时间
                    deployedApp.redeployResources.put(
                            contextXml.getAbsolutePath(),
                            Long.valueOf(contextXml.lastModified()));
//设置应用目录为重发布资源,记录最后修改的时间
                    deployedApp.redeployResources.put(docBase.getAbsolutePath(),
                            Long.valueOf(docBase.lastModified()));
//如果是war包,设置isExternalWar为true
                    if (docBase.getAbsolutePath().toLowerCase(Locale.ENGLISH).endsWith(".war")) {
                        isExternalWar = true;
                    }
                } else {
                    log.warn(sm.getString("hostConfig.deployDescriptor.localDocBaseSpecified",
                             docBase));
                    // Ignore specified docBase
                    context.setDocBase(null);
                }
            }

            host.addChild(context);//将context添加到对应host容器中,这里会进行context的初始化,启动生命周期
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            log.error(sm.getString("hostConfig.deployDescriptor.error",
                                   contextXml.getAbsolutePath()), t);
        } finally {
            // Get paths for WAR and expanded WAR in appBase

            // default to appBase dir + name 默认是AppBase路径加上容器的继承名称
            expandedDocBase = new File(host.getAppBaseFile(), cn.getBaseName());
//如果应用的docBase不为空,也就是设置了应用的位置,并且不是war包
            if (context.getDocBase() != null
                    && !context.getDocBase().toLowerCase(Locale.ENGLISH).endsWith(".war")) {
                // first assume docBase is absolute 重新设置expandedDocBase
                expandedDocBase = new File(context.getDocBase());
                if (!expandedDocBase.isAbsolute()) {
                    // if docBase specified and relative, it must be relative to appBase
//如果是相对路径,都会认为相对appbase
                    expandedDocBase = new File(host.getAppBaseFile(), context.getDocBase());
                }
            }

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

            // Add the eventual unpacked WAR and all the resources which will be
            // watched inside it
//如果是war包,并且允许解压,那么把war加入重新部署检测列表
            if (isExternalWar) {
                if (unpackWAR) {
                    deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
                            Long.valueOf(expandedDocBase.lastModified()));
//加入以expandedDocBase为基路径的资源重新加载监控
//在配置文件中有WatchedResource这样的xml元素,可以配置需要重加载检测文件
                    addWatchedResources(deployedApp, expandedDocBase.getAbsolutePath(), context);
                } else {
//如果不允许解压,那么就直接用他们的相对路径进行资源修改监控
                    addWatchedResources(deployedApp, null, context);
                }
            } else {
//如果不是war包,而又不是扩展目录应用,那么自动加上war后缀
                // Find an existing matching war and expanded folder
                if (!isExternal) {
                    File warDocBase = new File(expandedDocBase.getAbsolutePath() + ".war");
                    if (warDocBase.exists()) {
                        deployedApp.redeployResources.put(warDocBase.getAbsolutePath(),
                                Long.valueOf(warDocBase.lastModified()));
                    } else {
                        // Trigger a redeploy if a WAR is added 如果这个war后面被添加进来了,那么就触发重新加载
                        deployedApp.redeployResources.put(
                                warDocBase.getAbsolutePath(),
                                Long.valueOf(0));
                    }
                }
//这段代码和上面的war时的代码是一样的
                if (unpackWAR) {
                    deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
                            Long.valueOf(expandedDocBase.lastModified()));
                    addWatchedResources(deployedApp,
                            expandedDocBase.getAbsolutePath(), context);
                } else {
                    addWatchedResources(deployedApp, null, context);
                }
                if (!isExternal) {
                    // For external docBases, the context.xml will have been
                    // added above.
                    deployedApp.redeployResources.put(
                            contextXml.getAbsolutePath(),
                            Long.valueOf(contextXml.lastModified()));
                }
            }
            // Add the global redeploy resources (which are never deleted) at
            // the end so they don‘t interfere with the deletion process
//添加全局重新部署资源,如conf/engine名称+host名/context.xml.default和conf/context.xml
            addGlobalRedeployResources(deployedApp);
        }
//如果这个web应用已经成功添加到host中,那么记录这个应用已经被发布
        if (host.findChild(context.getName()) != null) {
            deployed.put(context.getName(), deployedApp);
        }

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

host.addChild最终调用了StandardHost.addChildInternal方法

private void addChildInternal(Container child) {

        if( log.isDebugEnabled() )
            log.debug("Add child " + child + " " + this);
        synchronized(children) {
            if (children.get(child.getName()) != null)
                throw new IllegalArgumentException("addChild:  Child name ‘" +
                                                   child.getName() +
                                                   "‘ is not unique");
            child.setParent(this);  // May throw IAE 给子容器设置父容器,并且触发属性更改事件
            children.put(child.getName(), child);将生成的context以context的名字做key,进行保存到map中
        }

        // Start child
        // Don‘t do this inside sync block - start can be a slow process and
        // locking the children object can cause problems elsewhere
        try {
            if ((getState().isAvailable() ||
                    LifecycleState.STARTING_PREP.equals(getState())) &&
                    startChildren) {
//启动context容器
                child.start();
            }
        } catch (LifecycleException e) {
            log.error("ContainerBase.addChild: start: ", e);
            throw new IllegalStateException("ContainerBase.addChild: start: " + e);
        } finally {
            fireContainerEvent(ADD_CHILD_EVENT, child);
        }
    }

child.setParent(this);

public void setParent(Container container) {

        Container oldParent = this.parent;
        this.parent = container;
//调用实现了PropertyChangeListener接口的观察者
        support.firePropertyChange("parent", oldParent, this.parent);

    }

child.start();
StandardContext.start();

 public final synchronized void start() throws LifecycleException {
        //如果已经正在启动了,那么无需重复启动,直接返回,由于多线程的原因,这个state是volatile修饰的,保证内存可见性
        if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
                LifecycleState.STARTED.equals(state)) {

            if (log.isDebugEnabled()) {
                Exception e = new LifecycleException();
                log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
            } else if (log.isInfoEnabled()) {
                log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
            }

            return;
        }
        //如果当前状态还是NEW,也就是连init都么有,那么就需要进行初始化
        if (state.equals(LifecycleState.NEW)) {
            init();
           //如果是失败,那么就停止容器
        } else if (state.equals(LifecycleState.FAILED)) {
            stop();
            //如果没有进行初始化完,就开始启动,那么直接报错
        } else if (!state.equals(LifecycleState.INITIALIZED) &&
                !state.equals(LifecycleState.STOPPED)) {
            invalidTransition(Lifecycle.BEFORE_START_EVENT);
        }

        try {
            //设置声明周期类型,并且触发对应的事件
            setStateInternal(LifecycleState.STARTING_PREP, null, false);
            startInternal();
            if (state.equals(LifecycleState.FAILED)) {
                // This is a ‘controlled‘ failure. The component put itself into the
                // FAILED state so call stop() to complete the clean-up.
                stop();
            } else if (!state.equals(LifecycleState.STARTING)) {
                // Shouldn‘t be necessary but acts as a check that sub-classes are
                // doing what they are supposed to.
                invalidTransition(Lifecycle.AFTER_START_EVENT);
            } else {
                setStateInternal(LifecycleState.STARTED, null, false);
            }
        } catch (Throwable t) {
            // This is an ‘uncontrolled‘ failure so put the component into the
            // FAILED state and throw an exception.
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
        }
    }

init();

//StandardContext触发after_init事件
public void lifecycleEvent(LifecycleEvent event) {

        // Identify the context we are associated with
        try {
            context = (Context) event.getLifecycle();
        } catch (ClassCastException e) {
            log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
            return;
        }

        // Process the event that has occurred
        if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
            configureStart();
        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
            beforeStart();-----》2
        } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
            // Restore docBase for management tools
            if (originalDocBase != null) {
                context.setDocBase(originalDocBase);
            }
        } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
            configureStop();
        } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
            init(); -----》1
        } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
            destroy();
        }

    }

ContextConfig.init();

protected void init() {
        // Called from StandardContext.init()
//创建一个Digester用于解析context.xml
        Digester contextDigester = createContextDigester();
        contextDigester.getParser();

        if (log.isDebugEnabled()) {
            log.debug(sm.getString("contextConfig.init"));
        }
        context.setConfigured(false);//设置配置状态,默认设置为失败,以免被误任务成功
        ok = true;

        contextConfig(contextDigester);
    }

ContextConfig.contextConfig()

protected void contextConfig(Digester digester) {

        String defaultContextXml = null;

        // Open the default context.xml file, if it exists 如果配置了默认的配置,使用它
        if (context instanceof StandardContext) {
            defaultContextXml = ((StandardContext)context).getDefaultContextXml();
        }
        // set the default if we don‘t have any overrides 如果没有使用tomcat默认的全局配置
        if (defaultContextXml == null) {
            defaultContextXml = Constants.DefaultContextXml;//conf/context.xml
        }
//如果还没有进行解析,那么就会重新解析,默认的全局配置-》configBase下的context.xml.default-》configBase下的配置
        if (!context.getOverride()) {
            File defaultContextFile = new File(defaultContextXml);
            if (!defaultContextFile.isAbsolute()) {
                defaultContextFile =
                        new File(context.getCatalinaBase(), defaultContextXml);
            }
            if (defaultContextFile.exists()) {
                try {
                    URL defaultContextUrl = defaultContextFile.toURI().toURL();
//处理应用配置,这个配置是默认配置,如果用户指定了默认配置,那么就解析用户指定的配置,如果没有指定,那么就直接使用tomcat提供的全局配置
                    processContextConfig(digester, defaultContextUrl);
                } catch (MalformedURLException e) {
                    log.error(sm.getString(
                            "contextConfig.badUrl", defaultContextFile), e);
                }
            }

//Constants.HostContextXml == context.xml.default,这个默认配置文件是host范围的context配置
            File hostContextFile = new File(getHostConfigBase(), Constants.HostContextXml);
            if (hostContextFile.exists()) {
                try {
                    URL hostContextUrl = hostContextFile.toURI().toURL();
                    processContextConfig(digester, hostContextUrl);
                } catch (MalformedURLException e) {
                    log.error(sm.getString(
                            "contextConfig.badUrl", hostContextFile), e);
                }
            }
        }
//context.getConfigFile() 获取应用的配置的文件,这个配置文件就是config/engine名+host名下扫描出来的配置文件
        if (context.getConfigFile() != null) {
            processContextConfig(digester, context.getConfigFile());
        }

    }

ContextConfig. processContextConfig()

//解析应用配置文件,从目前tomcat调用这个方法的先后顺序可以看出,tomcat给一个web应用设置属性是从全局默认的配置开始-》host级别的配置-》再到context级别的配置,所以context级别的配置最高。
protected void processContextConfig(Digester digester, URL contextXml) {

        if (log.isDebugEnabled()) {
            log.debug("Processing context [" + context.getName()
                    + "] configuration file [" + contextXml + "]");
        }

        InputSource source = null;
        InputStream stream = null;

        try {
            source = new InputSource(contextXml.toString());
            URLConnection xmlConn = contextXml.openConnection();
            xmlConn.setUseCaches(false);
            stream = xmlConn.getInputStream();
        } catch (Exception e) {
            log.error(sm.getString("contextConfig.contextMissing",
                      contextXml) , e);
        }

        if (source == null) {
            return;
        }

        try {
            source.setByteStream(stream);
            digester.setClassLoader(this.getClass().getClassLoader());
//这里这个digester不会再创建StandardContext对象了,因为在前面已经创建了一个
            digester.setUseContextClassLoader(false);
            digester.push(context.getParent());
            digester.push(context);
            XmlErrorHandler errorHandler = new XmlErrorHandler();
            digester.setErrorHandler(errorHandler);
            digester.parse(source);
            if (errorHandler.getWarnings().size() > 0 ||
                    errorHandler.getErrors().size() > 0) {
                errorHandler.logFindings(log, contextXml.toString());
                ok = false;
            }
            if (log.isDebugEnabled()) {
                log.debug("Successfully processed context [" + context.getName()
                        + "] configuration file [" + contextXml + "]");
            }
        } catch (SAXParseException e) {
            log.error(sm.getString("contextConfig.contextParse",
                    context.getName()), e);
            log.error(sm.getString("contextConfig.defaultPosition",
                             "" + e.getLineNumber(),
                             "" + e.getColumnNumber()));
            ok = false;
        } catch (Exception e) {
            log.error(sm.getString("contextConfig.contextParse",
                    context.getName()), e);
            ok = false;
        } finally {
            try {
                if (stream != null) {
                    stream.close();
                }
            } catch (IOException e) {
                log.error(sm.getString("contextConfig.contextClose"), e);
            }
        }
    }

//before_start事件
ContextConfig.beforeStart();

protected synchronized void beforeStart() {

        try {
            fixDocBase();
        } catch (IOException e) {
            log.error(sm.getString(
                    "contextConfig.fixDocBase", context.getName()), e);
        }

        antiLocking();
    }

ContextConfig.fixDocBase()
//对docBase做调整
protected void fixDocBase() throws IOException {

        Host host = (Host) context.getParent();
        File appBase = host.getAppBaseFile();

        String docBase = context.getDocBase();
//如果没有设置docBase,那么就根据应用路径重新设置路径
        if (docBase == null) {
            // Trying to guess the docBase according to the path
            String path = context.getPath();
            if (path == null) {
                return;
            }
//通过路径和版本去获取docBase
            ContextName cn = new ContextName(path, context.getWebappVersion());
            docBase = cn.getBaseName();
        }

        File file = new File(docBase);
        if (!file.isAbsolute()) {
            docBase = (new File(appBase, docBase)).getPath();
        } else {
            docBase = file.getCanonicalPath();
        }
        file = new File(docBase);
        String origDocBase = docBase;

        ContextName cn = new ContextName(context.getPath(), context.getWebappVersion());
        String pathName = cn.getBaseName();

        boolean unpackWARs = true;
        if (host instanceof StandardHost) {
            unpackWARs = ((StandardHost) host).isUnpackWARs();
            if (unpackWARs && context instanceof StandardContext) {
                unpackWARs =  ((StandardContext) context).getUnpackWAR();
            }
        }
//判断这个docBase路径是否是webapps下的
        boolean docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar);
//如果这个docBase指定的是一个war包,那么就将其解压
        if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory()) {
            URL war = UriUtil.buildJarUrl(new File(docBase));
            if (unpackWARs) {
//解压war包,返回解压后的目录路径,war解析源码请看war包解析笔记
                docBase = ExpandWar.expand(host, war, pathName);
                file = new File(docBase);
                docBase = file.getCanonicalPath();
                if (context instanceof StandardContext) {
//设置未解压时指定的位置,因为解压时会将解压后的内容放到host指定的appBase目录下
                    ((StandardContext) context).setOriginalDocBase(origDocBase);
                }
            } else {
//如果不需要解压,那么就校验对应的docbase是由已经存在了,如果不存在,直接报错
                ExpandWar.validate(host, war, pathName);
            }
        } else {
            File docDir = new File(docBase);
            File warFile = new File(docBase + ".war");
            URL war = null;
//如果war包存在,并且是在APPBase中的,那么直接获取其war的url
            if (warFile.exists() && docBaseInAppBase) {
                war = UriUtil.buildJarUrl(warFile);
            }
//如果目录存在,并且是war包,允许解压
            if (docDir.exists()) {
                if (war != null && unpackWARs) {
                    // Check if WAR needs to be re-expanded (e.g. if it has
                    // changed). Note: HostConfig.deployWar() takes care of
                    // ensuring that the correct XML file is used.
                    // This will be a NO-OP if the WAR is unchanged.
                    ExpandWar.expand(host, war, pathName);
                }
            } else {
                if (war != null) {
                    if (unpackWARs) {
                        docBase = ExpandWar.expand(host, war, pathName);
                        file = new File(docBase);
                        docBase = file.getCanonicalPath();
                    } else {
                        docBase = warFile.getCanonicalPath();
                        ExpandWar.validate(host, war, pathName);
                    }
                }
                if (context instanceof StandardContext) {
                    ((StandardContext) context).setOriginalDocBase(origDocBase);
                }
            }
        }

        // Re-calculate now docBase is a canonical path
        docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar);
//如果目录为appBase下的,那么直接截取到appBase后面一截
        if (docBaseInAppBase) {
            docBase = docBase.substring(appBase.getPath().length());
            docBase = docBase.replace(File.separatorChar, ‘/‘);
            if (docBase.startsWith("/")) {
                docBase = docBase.substring(1);
            }
        } else {
            docBase = docBase.replace(File.separatorChar, ‘/‘);
        }

        context.setDocBase(docBase);
    }

 protected synchronized void startInternal() throws LifecycleException {
        setConfigured(false);
        boolean ok = true;

        // Currently this is effectively a NO-OP but needs to be called to
        // ensure the NamingResources follows the correct lifecycle
        if (namingResources != null) {
            namingResources.start();
        }

        // Post work directory 生成工作目录,这个目录用于存放编译后的jsp文件,一般生成的目录格式为tomcat安装目录work+engine名+host名+context的baseName
        postWorkDirectory();

        // Add missing components as necessary
        if (getResources() == null) {   // (1) Required by Loader
            if (log.isDebugEnabled())
                log.debug("Configuring default Resources");

            try {
                setResources(new StandardRoot(this));
            } catch (IllegalArgumentException e) {
                log.error(sm.getString("standardContext.resourcesInit"), e);
                ok = false;
            }
        }
        if (ok) {
            resourcesStart();
        }
//设置web加载器
        if (getLoader() == null) {
            WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
            webappLoader.setDelegate(getDelegate());
            setLoader(webappLoader);
        }

       。。。省略代码

                // Notify our interested LifecycleListeners
                fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);

                // Start our child containers, if not already started,开始子容器context的子容器是wrapper
                for (Container child : findChildren()) {
                    if (!child.getState().isAvailable()) {
                        child.start();
                    }
                }

                // Start the Valves in our pipeline (including the basic),
                // if any
                if (pipeline instanceof Lifecycle) {
                    ((Lifecycle) pipeline).start();
                }
//省略了集群管理代码

                // Configure default manager if none was specified
                if (contextManager != null) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("standardContext.manager",
                                contextManager.getClass().getName()));
                    }
                    setManager(contextManager);
                }
// Set up the context init params 设置初始化参数
            mergeParameters();

            // Call ServletContainerInitializers
            for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
                initializers.entrySet()) {
                try {
                    entry.getKey().onStartup(entry.getValue(),
                            getServletContext());
                } catch (ServletException e) {
                    log.error(sm.getString("standardContext.sciFail"), e);
                    ok = false;
                    break;
                }
            }

            // Configure and call application event listeners 实例化监听器,并且调用实现了ServletContextListener监听器的初始化方法
            if (ok) {
                if (!listenerStart()) {
                    log.error(sm.getString("standardContext.listenerFail"));
                    ok = false;
                }
            }

            // Check constraints for uncovered HTTP methods
            // Needs to be after SCIs and listeners as they may programmatically
            // change constraints
            if (ok) {
                checkConstraintsForUncoveredMethods(findConstraints());
            }

            try {
                // Start manager session管理器
                Manager manager = getManager();
                if (manager instanceof Lifecycle) {
                    ((Lifecycle) manager).start();
                }
            } catch(Exception e) {
                log.error(sm.getString("standardContext.managerFail"), e);
                ok = false;
            }

            // Configure and call application filters 实例化filter,并且调用其初始化方法
            if (ok) {
                if (!filterStart()) {
                    log.error(sm.getString("standardContext.filterFail"));
                    ok = false;
                }
            }

            // Load and initialize all "load on startup" servlets 实例化设置了load on startup的Servlet并调用初始化方法
            if (ok) {
                if (!loadOnStartup(findChildren())){
                    log.error(sm.getString("standardContext.servletFail"));
                    ok = false;
                }
            }

            // Start ContainerBackgroundProcessor thread
            super.threadStart();
        } finally {
            // Unbinding thread
            unbindThread(oldCCL);
        }

        // Set available status depending upon startup success
        if (ok) {
            if (log.isDebugEnabled())
                log.debug("Starting completed");
        } else {
            log.error(sm.getString("standardContext.startFailed", getName()));
        }

        startTime=System.currentTimeMillis();

        // Send j2ee.state.running notification
        if (ok && (this.getObjectName() != null)) {
            Notification notification =
                new Notification("j2ee.state.running", this.getObjectName(),
                                 sequenceNumber.getAndIncrement());
            broadcaster.sendNotification(notification);
        }

        // The WebResources implementation caches references to JAR files. On
        // some platforms these references may lock the JAR files. Since web
        // application start is likely to have read from lots of JARs, trigger
        // a clean-up now.
        getResources().gc();

        // Reinitializing if something went wrong
        if (!ok) {
            setState(LifecycleState.FAILED);
        } else {
            setState(LifecycleState.STARTING);
        }
    }

configure_start事件
ContextConfig.configureStart()

protected synchronized void configureStart() {
        // Called from StandardContext.start()
//开始web.xml的配置
        webConfig();

//如果未配置忽略应用注解配置,那么对filter,servlet,listener进行Resource注解的搜索
Resource配置在类,字段,方法上,会根据资源的类型进行划分资源类型,添加到不同资源集合中,比如环境变量,JNDI等等资源
        if (!context.getIgnoreAnnotations()) {
            applicationAnnotationsConfig();
        }
        if (ok) {
//将context约束的角色和wrapper中注解RunAs或配置中设置的角色添加到context容器中,重复的不会被再次添加
            validateSecurityRoles();
        }

        // Configure an authenticator if we need one
//配置验证器,如果没有Ralm或者实现了 Authenticator的管道阀,那么就会添加一个默认的NonLoginAuthenticator验证器
        if (ok) {
            authenticatorConfig();
        }
        // Make our application available if no problems were encountered
//如果配置context时没有遇到任何问题,那么就表示配置成功
        if (ok) {
            context.setConfigured(true);
        } else {
            log.error(sm.getString("contextConfig.unavailable"));
            context.setConfigured(false);
        }

    }

ContextConfig.webConfig();

 protected void webConfig() {
       //new出一个webxml的解析器
        WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
                context.getXmlValidation(), context.getXmlBlockExternal());

        Set<WebXml> defaults = new HashSet<>();
//获取webxml片段
        defaults.add(getDefaultWebXmlFragment(webXmlParser));

        WebXml webXml = createWebXml();

        // Parse context level web.xml
        InputSource contextWebXml = getContextWebXmlSource();
        if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
            ok = false;
        }

        ServletContext sContext = context.getServletContext();

        // Ordering is important here

        // Step 1. Identify all the JARs packaged with the application and those
        // provided by the container. If any of the application JARs have a
        // web-fragment.xml it will be parsed at this point. web-fragment.xml
        // files are ignored for container provided JARs.
//解析应用程序jar包中META-INF/web-fragment.xml
//key为jar包的全路径名,value是从META-INF/web-fragment.xml解析后的WebXml对象
        Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);

        // Step 2. Order the fragments. 对片段进行排序,因为在片段中可以设置依赖,在什么什么之前启动,什么什么之后启动。
        Set<WebXml> orderedFragments = null;
        orderedFragments =
                WebXml.orderWebFragments(webXml, fragments, sContext);

        // Step 3. Look for ServletContainerInitializer implementations
        if (ok) {
//查找META-INF/services/javax.servlet.ServletContainerInitializer配置文件,获取ServletContainerInitializer,这里的ServletContainerInitializer可以使用HandlesTypes注解指定需要处理的类型
            processServletContainerInitializers();
        }

        if  (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
            // Step 4. Process /WEB-INF/classes for annotations and
            // @HandlesTypes matches
            Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>();

            if (ok) {
//加载/WEB-INF/classes下的资源
                WebResource[] webResources =
                        context.getResources().listResources("/WEB-INF/classes");

                for (WebResource webResource : webResources) {
                    // Skip the META-INF directory from any JARs that have been
                    // expanded in to WEB-INF/classes (sometimes IDEs do this).
                    if ("META-INF".equals(webResource.getName())) {
                        continue;
                    }
//使用自定义的字节码解析器去解析class文件,寻找有@WebServlet,@WebFilter,@WebListener,然后将解析好的ServletDef,FilterDef,Listener注入到WebXml对象当中
                    processAnnotationsWebResource(webResource, webXml,
                            webXml.isMetadataComplete(), javaClassCache);
                }
            }

            // Step 5. Process JARs for annotations and
            // @HandlesTypes matches - only need to process those fragments we
            // are going to use (remember orderedFragments includes any
            // container fragments)
//解析jar包中的注解Servlet,Listener,FIlter
            if (ok) {
                processAnnotations(
                        orderedFragments, webXml.isMetadataComplete(), javaClassCache);
            }

            // Cache, if used, is no longer required so clear it
            javaClassCache.clear();
        }

        if (!webXml.isMetadataComplete()) {
            // Step 6. Merge web-fragment.xml files into the main web.xml
//合并片段配置文件的内容到主web.xml中
            // file.
            if (ok) {
                ok = webXml.merge(orderedFragments);
            }

            // Step 7. Apply global defaults
            // Have to merge defaults before JSP conversion since defaults
            // provide JSP servlet definition. //应用全局默认配置
            webXml.merge(defaults);

            // Step 8. Convert explicitly mentioned jsps to servlets
//准备数据转换被显示配置的jsp为servlet,指定初始化参数,指定ServletClass为org.apache.jasper.servlet.JspServlet
            if (ok) {
                convertJsps(webXml);
            }

            // Step 9. Apply merged web.xml to Context  将web.xml配置的数据设置到context中
            if (ok) {
                configureContext(webXml);
            }
        } else {
            webXml.merge(defaults);
            convertJsps(webXml);
            configureContext(webXml);
        }

        if (context.getLogEffectiveWebXml()) {
            log.info("web.xml:\n" + webXml.toXml());
        }

        // Always need to look for static resources
        // Step 10. Look for static resources packaged in JARs 从jar中中查找静态资源
        if (ok) {
            // Spec does not define an order.
            // Use ordered JARs followed by remaining JARs
            Set<WebXml> resourceJars = new LinkedHashSet<>();
            for (WebXml fragment : orderedFragments) {
                resourceJars.add(fragment);
            }
            for (WebXml fragment : fragments.values()) {
                if (!resourceJars.contains(fragment)) {
                    resourceJars.add(fragment);
                }
            }
            processResourceJARs(resourceJars);
            // See also StandardContext.resourcesStart() for
            // WEB-INF/classes/META-INF/resources configuration
        }

        // Step 11. Apply the ServletContainerInitializer config to the
        // context 应用ServletContainerInitializer  Initializer --》要处理的类型(可能是注解标识的,也可能就是指定的类型)
        if (ok) {
            for (Map.Entry<ServletContainerInitializer,
                    Set<Class<?>>> entry :
                        initializerClassMap.entrySet()) {
                if (entry.getValue().isEmpty()) {
                    context.addServletContainerInitializer(
                            entry.getKey(), null);
                } else {
                    context.addServletContainerInitializer(
                            entry.getKey(), entry.getValue());
                }
            }
        }
    }

ContextConfig.getDefaultWebXmlFragment

private WebXml getDefaultWebXmlFragment(WebXmlParser webXmlParser) {

        // Host should never be null
        Host host = (Host) context.getParent();

        DefaultWebXmlCacheEntry entry = hostWebXmlCache.get(host);
//获取全局webxml配置文件,config/web.xml
        InputSource globalWebXml = getGlobalWebXmlSource();
//获取host级别的xml配置文件,文件名为web.xml.default
        InputSource hostWebXml = getHostWebXmlSource();

        long globalTimeStamp = 0;
        long hostTimeStamp = 0;

        if (globalWebXml != null) {
            URLConnection uc = null;
            try {
//获取systemid对应数据的最后修改时间
                URL url = new URL(globalWebXml.getSystemId());
                uc = url.openConnection();
                globalTimeStamp = uc.getLastModified();
            } catch (IOException e) {
                globalTimeStamp = -1;
            } finally {
                if (uc != null) {
                    try {
                        uc.getInputStream().close();
                    } catch (IOException e) {
                        ExceptionUtils.handleThrowable(e);
                        globalTimeStamp = -1;
                    }
                }
            }
        }

        if (hostWebXml != null) {
            URLConnection uc = null;
            try {
//获取systemid对应数据的最后修改时间
                URL url = new URL(hostWebXml.getSystemId());
                uc = url.openConnection();
                hostTimeStamp = uc.getLastModified();
            } catch (IOException e) {
                hostTimeStamp = -1;
            } finally {
                if (uc != null) {
                    try {
                        uc.getInputStream().close();
                    } catch (IOException e) {
                        ExceptionUtils.handleThrowable(e);
                        hostTimeStamp = -1;
                    }
                }
            }
        }
//如果已经解析过了,那么关闭资源,直接返回已经解析好的WebXml
        if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
                entry.getHostTimeStamp() == hostTimeStamp) {
            InputSourceUtil.close(globalWebXml);
            InputSourceUtil.close(hostWebXml);
            return entry.getWebXml();
        }

        // Parsing global web.xml is relatively expensive. Use a sync block to
        // make sure it only happens once. Use the pipeline since a lock will
        // already be held on the host by another thread
        synchronized (host.getPipeline()) {
            entry = hostWebXmlCache.get(host);
            if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
                    entry.getHostTimeStamp() == hostTimeStamp) {
                return entry.getWebXml();
            }
//直接new出一个WebXml对象
            WebXml webXmlDefaultFragment = createWebXml();
            webXmlDefaultFragment.setOverridable(true);
            // Set to distributable else every app will be prevented from being
            // distributable when the default fragment is merged with the main
            // web.xml
            webXmlDefaultFragment.setDistributable(true);
            // When merging, the default welcome files are only used if the app has
            // not defined any welcomes files.
            webXmlDefaultFragment.setAlwaysAddWelcomeFiles(false);

            // Parse global web.xml if present
            if (globalWebXml == null) {
                // This is unusual enough to log
                log.info(sm.getString("contextConfig.defaultMissing"));
            } else {
//解析全局web配置
                if (!webXmlParser.parseWebXml(
                        globalWebXml, webXmlDefaultFragment, false)) {
                    ok = false;
                }
            }

            // Parse host level web.xml if present
            // Additive apart from welcome pages
            webXmlDefaultFragment.setReplaceWelcomeFiles(true);
//解析host级别的webxml,会覆盖掉全局的相同属性值的内容
            if (!webXmlParser.parseWebXml(
                    hostWebXml, webXmlDefaultFragment, false)) {
                ok = false;
            }

            // Don‘t update the cache if an error occurs
            if (globalTimeStamp != -1 && hostTimeStamp != -1) {
                entry = new DefaultWebXmlCacheEntry(webXmlDefaultFragment,
                        globalTimeStamp, hostTimeStamp);
                hostWebXmlCache.put(host, entry);
            }

            return webXmlDefaultFragment;
        }
    }

WebXmlParser.parseWebXml(InputSource source, WebXml dest,
            boolean fragment)

public boolean parseWebXml(InputSource source, WebXml dest,
            boolean fragment) {

        boolean ok = true;

        if (source == null) {
            return ok;
        }

        XmlErrorHandler handler = new XmlErrorHandler();

        Digester digester;
        WebRuleSet ruleSet;
        if (fragment) {//如果是web.xml配置片段,那么就使用片段Digester
            digester = webFragmentDigester;
            ruleSet = webFragmentRuleSet;
        } else {
            digester = webDigester;
            ruleSet = webRuleSet;
        }

        digester.push(dest);
        digester.setErrorHandler(handler);
        try {
            digester.parse(source);
//如果解析出了错误,那么设置ok为false,意味着context启动失败
            if (handler.getWarnings().size() > 0 ||
                    handler.getErrors().size() > 0) {
                ok = false;
                handler.logFindings(log, source.getSystemId());
            }
        } catch (SAXParseException e) {
            log.error(sm.getString("webXmlParser.applicationParse",
                    source.getSystemId()), e);
            log.error(sm.getString("webXmlParser.applicationPosition",
                             "" + e.getLineNumber(),
                             "" + e.getColumnNumber()));
            ok = false;
        } catch (Exception e) {
            log.error(sm.getString("webXmlParser.applicationParse",
                    source.getSystemId()), e);
            ok = false;
        } finally {
            InputSourceUtil.close(source);
            digester.reset();
            ruleSet.recycle();
        }

        return ok;
    }

Digester

public WebXmlParser(boolean namespaceAware, boolean validation,
            boolean blockExternal) {
        webRuleSet = new WebRuleSet(false);
        webDigester = DigesterFactory.newDigester(validation,
                namespaceAware, webRuleSet, blockExternal);
        webDigester.getParser();

        webFragmentRuleSet = new WebRuleSet(true);
        webFragmentDigester = DigesterFactory.newDigester(validation,
                namespaceAware, webFragmentRuleSet, blockExternal);
        webFragmentDigester.getParser();
    }

public WebRuleSet(String prefix, boolean fragment) {
        super();
        this.prefix = prefix;
        this.fragment = fragment;

        if(fragment) {
            fullPrefix = prefix + "web-fragment";
        } else {
            fullPrefix = prefix + "web-app";
        }

        absoluteOrdering = new AbsoluteOrderingRule(fragment);
        relativeOrdering = new RelativeOrderingRule(fragment);
    }

private void configureContext(WebXml webxml) {
        // As far as possible, process in alphabetical order so it is easy to
        // check everything is present
        // Some validation depends on correct public ID
        context.setPublicId(webxml.getPublicId());

        // Everything else in order
        context.setEffectiveMajorVersion(webxml.getMajorVersion());
        context.setEffectiveMinorVersion(webxml.getMinorVersion());
//将上下文参数添加到context中
        for (Entry<String, String> entry : webxml.getContextParams().entrySet()) {
            context.addParameter(entry.getKey(), entry.getValue());
        }
        context.setDenyUncoveredHttpMethods(
                webxml.getDenyUncoveredHttpMethods());
        context.setDisplayName(webxml.getDisplayName());
        context.setDistributable(webxml.isDistributable());
//EJB相关的引用资源,反正我不懂
        for (ContextLocalEjb ejbLocalRef : webxml.getEjbLocalRefs().values()) {
            context.getNamingResources().addLocalEjb(ejbLocalRef);
        }
        for (ContextEjb ejbRef : webxml.getEjbRefs().values()) {
            context.getNamingResources().addEjb(ejbRef);
        }
//
        for (ContextEnvironment environment : webxml.getEnvEntries().values()) {
            context.getNamingResources().addEnvironment(environment);
        }
//添加错误页面
        for (ErrorPage errorPage : webxml.getErrorPages().values()) {
            context.addErrorPage(errorPage);
        }
//添加过滤器,filterName->filterDef
        for (FilterDef filter : webxml.getFilters().values()) {
            if (filter.getAsyncSupported() == null) {
                filter.setAsyncSupported("false");
            }
            context.addFilterDef(filter);
        }
//FilterMap urlPattern与Servlet的关联
        for (FilterMap filterMap : webxml.getFilterMappings()) {
            context.addFilterMap(filterMap);
        }
        context.setJspConfigDescriptor(webxml.getJspConfigDescriptor());
//添加监听器到context的数组中
        for (String listener : webxml.getListeners()) {
            context.addApplicationListener(listener);
        }
//添加locale-》字符集
        for (Entry<String, String> entry :
                webxml.getLocaleEncodingMappings().entrySet()) {
            context.addLocaleEncodingMappingParameter(entry.getKey(),
                    entry.getValue());
        }
        // Prevents IAE  设置登录配置
        if (webxml.getLoginConfig() != null) {
            context.setLoginConfig(webxml.getLoginConfig());
        }
//消息目标引用
        for (MessageDestinationRef mdr :
                webxml.getMessageDestinationRefs().values()) {
            context.getNamingResources().addMessageDestinationRef(mdr);
        }

        // messageDestinations were ignored in Tomcat 6, so ignore here

        context.setIgnoreAnnotations(webxml.isMetadataComplete());
//设置媒体类型
        for (Entry<String, String> entry :
                webxml.getMimeMappings().entrySet()) {
            context.addMimeMapping(entry.getKey(), entry.getValue());
        }
        // Name is just used for ordering 添加JNDI引用
        for (ContextResourceEnvRef resource :
                webxml.getResourceEnvRefs().values()) {
            context.getNamingResources().addResourceEnvRef(resource);
        }
//添加JNDI
        for (ContextResource resource : webxml.getResourceRefs().values()) {
            context.getNamingResources().addResource(resource);
        }
//设置权限
        boolean allAuthenticatedUsersIsAppRole =
                webxml.getSecurityRoles().contains(
                        SecurityConstraint.ROLE_ALL_AUTHENTICATED_USERS);
        for (SecurityConstraint constraint : webxml.getSecurityConstraints()) {
            if (allAuthenticatedUsersIsAppRole) {
                constraint.treatAllAuthenticatedUsersAsApplicationRole();
            }
            context.addConstraint(constraint);
        }
//添加角色
        for (String role : webxml.getSecurityRoles()) {
            context.addSecurityRole(role);
        }
//添加服务引用
        for (ContextService service : webxml.getServiceRefs().values()) {
            context.getNamingResources().addService(service);
        }
//添加ServletDef,包装成StandardWrapper容器
        for (ServletDef servlet : webxml.getServlets().values()) {
            Wrapper wrapper = context.createWrapper();
            // Description is ignored
            // Display name is ignored
            // Icons are ignored

            // jsp-file gets passed to the JSP Servlet as an init-param
//设置启动顺序
            if (servlet.getLoadOnStartup() != null) {
                wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
            }
            if (servlet.getEnabled() != null) {
                wrapper.setEnabled(servlet.getEnabled().booleanValue());
            }
            wrapper.setName(servlet.getServletName());
            Map<String,String> params = servlet.getParameterMap();
            for (Entry<String, String> entry : params.entrySet()) {
                wrapper.addInitParameter(entry.getKey(), entry.getValue());
            }
            wrapper.setRunAs(servlet.getRunAs());
            Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();
            for (SecurityRoleRef roleRef : roleRefs) {
                wrapper.addSecurityReference(
                        roleRef.getName(), roleRef.getLink());
            }
            wrapper.setServletClass(servlet.getServletClass());
            MultipartDef multipartdef = servlet.getMultipartDef();
//设置上传文件的大小
            if (multipartdef != null) {
                if (multipartdef.getMaxFileSize() != null &&
                        multipartdef.getMaxRequestSize()!= null &&
                        multipartdef.getFileSizeThreshold() != null) {
                    wrapper.setMultipartConfigElement(new MultipartConfigElement(
                            multipartdef.getLocation(),
                            Long.parseLong(multipartdef.getMaxFileSize()),
                            Long.parseLong(multipartdef.getMaxRequestSize()),
                            Integer.parseInt(
                                    multipartdef.getFileSizeThreshold())));
                } else {
                    wrapper.setMultipartConfigElement(new MultipartConfigElement(
                            multipartdef.getLocation()));
                }
            }
//是否异步支持
            if (servlet.getAsyncSupported() != null) {
                wrapper.setAsyncSupported(
                        servlet.getAsyncSupported().booleanValue());
            }
            wrapper.setOverridable(servlet.isOverridable());
//添加子容器,初始化和启动子容器
            context.addChild(wrapper);
        }
//添加ServletMapping urlPattern->servletName
        for (Entry<String, String> entry :
                webxml.getServletMappings().entrySet()) {
            context.addServletMappingDecoded(entry.getKey(), entry.getValue());
        }
//获取session配置
        SessionConfig sessionConfig = webxml.getSessionConfig();
        if (sessionConfig != null) {
            if (sessionConfig.getSessionTimeout() != null) {
                context.setSessionTimeout(
                        sessionConfig.getSessionTimeout().intValue());
            }
            SessionCookieConfig scc =
                context.getServletContext().getSessionCookieConfig();
            scc.setName(sessionConfig.getCookieName());
            scc.setDomain(sessionConfig.getCookieDomain());
            scc.setPath(sessionConfig.getCookiePath());
            scc.setComment(sessionConfig.getCookieComment());
            if (sessionConfig.getCookieHttpOnly() != null) {
                scc.setHttpOnly(sessionConfig.getCookieHttpOnly().booleanValue());
            }
            if (sessionConfig.getCookieSecure() != null) {
                scc.setSecure(sessionConfig.getCookieSecure().booleanValue());
            }
            if (sessionConfig.getCookieMaxAge() != null) {
                scc.setMaxAge(sessionConfig.getCookieMaxAge().intValue());
            }
            if (sessionConfig.getSessionTrackingModes().size() > 0) {
                context.getServletContext().setSessionTrackingModes(
                        sessionConfig.getSessionTrackingModes());
            }
        }

        // Context doesn‘t use version directly
//添加欢迎配置
        for (String welcomeFile : webxml.getWelcomeFiles()) {
            /*
             * The following will result in a welcome file of "" so don‘t add
             * that to the context
             * <welcome-file-list>
             *   <welcome-file/>
             * </welcome-file-list>
             */
            if (welcomeFile != null && welcomeFile.length() > 0) {
                context.addWelcomeFile(welcomeFile);
            }
        }

        // Do this last as it depends on servlets
        for (JspPropertyGroup jspPropertyGroup :
                webxml.getJspPropertyGroups()) {
            String jspServletName = context.findServletMapping("*.jsp");
            if (jspServletName == null) {
                jspServletName = "jsp";
            }
            if (context.findChild(jspServletName) != null) {
                for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
                    context.addServletMappingDecoded(urlPattern, jspServletName, true);
                }
            } else {
                if(log.isDebugEnabled()) {
                    for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
                        log.debug("Skipping " + urlPattern + " , no servlet " +
                                jspServletName);
                    }
                }
            }
        }
//添加初始方法
        for (Entry<String, String> entry :
                webxml.getPostConstructMethods().entrySet()) {
            context.addPostConstructMethod(entry.getKey(), entry.getValue());
        }
//添加销毁方法
        for (Entry<String, String> entry :
            webxml.getPreDestroyMethods().entrySet()) {
            context.addPreDestroyMethod(entry.getKey(), entry.getValue());
        }
    }

原文地址:https://www.cnblogs.com/honger/p/10362774.html

时间: 2024-10-09 18:10:11

context创建过程解析(一)之deployDescriptors的相关文章

Android Context创建过程

    特定的资源或者类构成了Android应用程序的运行上下文环境 PackageManager, ClassLoader, Assert等等 Android应用程序窗口的运行上下文环境是通过ContextImpl类来描述的,即每一个Activity组件都关联有一个ContextImpl对象.ContextImpl类继承了Context类,它与Activity组件的关系如图 这个类图在设计模式里面就可以称为装饰模式 Activity组件以后就可以通过这个ContextImpl对象来执行一些具体

Andorid DexClassLoader的创建过程解析(基于5.0)

做Android插件框架时,经常会用到dex的动态加载,就要直接或间接的使用DexClassLoader,在new DexClassLoader的时候Android系统做了很多工作,下面我们详细分析一下: public class DexClassLoader extends BaseDexClassLoader { public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, Class

Activtiy完全解析(一、Activity的创建过程)

??在Android开发过程中,我们几乎每天都在跟Activity打交道.我们循规蹈矩的调用startActivity()方法便可以打开一个新的界面,但是这个界面是怎样从无到有我们却不是很清楚,也有很多人根本就没有想过.我们写的layout布局通过setContentView()方法是怎样加载到activity中,然后又是怎样显示到界面上的?这一系列的问题相信大多数人都不清楚. ??也许是因为我们平时的开发过程根本不需要了解这些,但是如果深入了解后,对我们还是很有帮助的.接下来我们用3篇文章去分

Spring源码解析 – AnnotationConfigApplicationContext容器创建过程

Spring在BeanFactory基础上提供了一些列具体容器的实现,其中AnnotationConfigApplicationContext是一个用来管理注解bean的容器,从AnnotationConfigApplicationContext的实现结构图中可以看出: AnnotationConfigApplicationContext继承GenericApplicationContext这个通用应用上下文,GenericApplicationContext内部定义了一个DefaultList

android源码解析(十六)--&gt;应用进程Context创建流程

今天讲讲应用进程Context的创建流程,相信大家平时在开发过程中经常会遇到对Context对象的使用,Application是Context,Activity是Context,Service也是Context,所以有一个经典的问题是一个App中一共有多少个Context? 这个问题的答案是Application + N个Activity + N个Service. 还有就是我们平时在使用Context过程中许多时候由于使用不当,可能会造成内存泄露的情况等等,这个也是需要我们注意的.这里有篇不错的

android 自定义View过程解析

PS:本篇文章大多数翻译自github上一篇英文文章! 总所周知,安卓UI是基于View(屏幕上的单一节点)和ViewGroup(屏幕上节点的集合),在android中有很多widgets和layouts可以用于创建UI界面,比如最常见的View有Button,TextView等等,而最常见的布局也有RelativeLayout,LinearLayout等. 在一些应用中我们不得不自定义View去满足我们的需求,自定义View可以继承一个View或者已存在的子类去创建我们自己的自定义View,甚

mybatis缓存创建过程

带着 上篇 的问题,再来看看mybatis的创建过程 1.从SqlSessionFactoryBuilder解析mybatis-config.xml开始 对文件流解析 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse());//等同于return new DefaultSqlSessionFactory(config); 关

Android从xml加载到View对象过程解析

我们从Activity的setContentView()入手,开始源码解析, //Activity.setContentView public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); initActionBar(); } //PhoneWindow.setContentView public void setContentView(int layoutResID) { if (

MyBatis初始化过程解析----广西11选5平台出租源码解析

准备工作 为了看清楚广西11选5平台出租的 Q1446595067 整个初始化过程,先创建一个简单的Java项目,目录结构如下图所示: 1.1 Product 产品实体类 public class Product { private long id; private String productName; private String productContent; private String price; private int sort; private int falseSales; p