zoukankan      html  css  js  c++  java
  • 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:
    " + 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());
            }
        }
  • 相关阅读:
    Zend Framework 2.1.5 中根据服务器的环境配置调用数据库等的不同配置
    在基于 Eclipse 的 IDE 中安装和使用 Emmet(ZenCoding)
    【翻译】Emmet(Zen Coding)官方文档 之六 自定义 Emmet
    【翻译】Emmet(Zen Coding)官方文档 之二 缩写
    【翻译】Emmet(Zen Coding)官方文档 之七 一览表
    【翻译】Emmet(Zen Coding)官方文档 之三 CSS 缩写
    【翻译】Emmet(Zen Coding)官方文档 之四 动作
    【翻译】Emmet(Zen Coding)官方文档 之一 web 编程的必备工具
    Zend Framework 2 时区设置警告问题的解决
    【翻译】Emmet (Zen Coding) 元素类型
  • 原文地址:https://www.cnblogs.com/honger/p/10362774.html
Copyright © 2011-2022 走看看