总结:主要是创建Context对象,并且将默认context配置,host级别配置,context配置的值设置进去,设置docBase,如果是war包就解压到webapp的目录中,重新设置docBase为war包解压后的目录。如果配置文件中没有指定docBase,那么就以webapps为基路径+context的baseName作为docBaseHostConfig.deployApps()//在监听到start事件类型,也就是StandardHost调用startInternalprotected 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); }deployDescriptorsprotected void deployDescriptors(File configBase, String[] files) { if (files == null) return; ExecutorService es = host.getStartStopExecutor();//获取线程池 List> 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 >> 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 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 fragments = processJarsForWebFragments(webXml, webXmlParser); // Step 2. Order the fragments. 对片段进行排序,因为在片段中可以设置依赖,在什么什么之前启动,什么什么之后启动。 Set 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 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 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 >> entry : initializerClassMap.entrySet()) { if (entry.getValue().isEmpty()) { context.addServletContainerInitializer( entry.getKey(), null); } else { context.addServletContainerInitializer( entry.getKey(), entry.getValue()); } } } }ContextConfig.getDefaultWebXmlFragmentprivate 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; }Digesterpublic 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 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 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 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 params = servlet.getParameterMap(); for (Entry entry : params.entrySet()) { wrapper.addInitParameter(entry.getKey(), entry.getValue()); } wrapper.setRunAs(servlet.getRunAs()); Set 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 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 * * */ 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* entry : webxml.getPostConstructMethods().entrySet()) { context.addPostConstructMethod(entry.getKey(), entry.getValue()); }//添加销毁方法 for (Entry entry : webxml.getPreDestroyMethods().entrySet()) { context.addPreDestroyMethod(entry.getKey(), entry.getValue()); } }