Tomcat源码分析(二):Context启动

Context启动

因为Context对应着应用,因此Context启动的主要部分就是部署应用
主要看下StandardContext的startInternal方法,因为该方法比较长,所以主要看几个关键的部分

protected synchronized void startInternal() throws LifecycleException {
	// 这里会创建一个专门用来加载应用程序的Loader
	if (getLoader() == null) {
      	 WebappLoader webappLoader = new WebappLoader();
         webappLoader.setDelegate(getDelegate());
         // 将这个Loader和Context关联起来
         setLoader(webappLoader);
     }
	 // 获取当前的线程上下文加载器
     ClassLoader oldCCL = bindThread();
     
     // 启动这个loader
     Loader loader = getLoader();
     if (loader instanceof Lifecycle) {
         ((Lifecycle) loader).start();
     }
     // 将新创建的WebappLoader绑定到当前线程
     unbindThread(oldCCL);
     oldCCL = bindThread();
	 
	 // 发送CONFIGURE_START_EVENT,这个事件会触发Wrapper的创建,并且和当前Context建立关联
	 fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
	
	// 启动子容器,也就是Wrapper,每个Wrapper代表一个servlet
	for (Container child : findChildren()) {
     if (!child.getState().isAvailable()) {
            child.start();
        }
    }
	
	// 启动pipeline
	if (pipeline instanceof Lifecycle) {
        ((Lifecycle) pipeline).start();
    }

	// 恢复成以前的线程上下文加载器
	unbindThread(oldCCL);
}

这里我们看下Context生命周期监听器ContextConfig在接收到CONFIGURE_START_EVENT时进行的操作

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();
   } 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();
   } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
       destroy();
   }

}
protected synchronized void configureStart() {
    // Called from StandardContext.start()

    if (log.isDebugEnabled()) {
        log.debug(sm.getString("contextConfig.start"));
    }

    if (log.isDebugEnabled()) {
        log.debug(sm.getString("contextConfig.xmlSettings",
                context.getName(),
                Boolean.valueOf(context.getXmlValidation()),
                Boolean.valueOf(context.getXmlNamespaceAware())));
    }

	// 解析web.xml文件,在这里会创建Wrapper
    webConfig();
    context.addServletContainerInitializer(new JasperInitializer(),null);


    if (!context.getIgnoreAnnotations()) {
        applicationAnnotationsConfig();
    }
    if (ok) {
        validateSecurityRoles();
    }

    // Configure an authenticator if we need one
    if (ok) {
        authenticatorConfig();
    }

    // Dump the contents of this pipeline if requested
    if (log.isDebugEnabled()) {
        log.debug("Pipeline Configuration:");
        Pipeline pipeline = context.getPipeline();
        Valve valves[] = null;
        if (pipeline != null) {
            valves = pipeline.getValves();
        }
        if (valves != null) {
            for (Valve valve : valves) {
                log.debug("  " + valve.getClass().getName());
            }
        }
        log.debug("======================");
    }

    // Make our application available if no problems were encountered
    if (ok) {
        context.setConfigured(true);
    } else {
        log.error(sm.getString("contextConfig.unavailable"));
        context.setConfigured(false);
    }

}

web.xml文件的解析

这里主要看下webConfig

WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
                context.getXmlValidation(), context.getXmlBlockExternal());

    Set<WebXml> defaults = new HashSet<>();
    // getDefaultWebXmlFragment解析以下两个地方的web.xml文件
    // 1. 全局的web.xml文件,位置在${tomcat目录}/resource/conf/web.xml
    // 2. host层面的web.xml文件,位置在${tomcat目录}/resource/conf/Catalina/localhost/web.xml.default
    defaults.add(getDefaultWebXmlFragment(webXmlParser));

    WebXml webXml = createWebXml();

    // Parse context level web.xml
    // 解析context层级也就是应用层级的web.xml
    // ${tomcat目录}/resource/webapps/${应用}/WEB-INF/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.
    // 解析应用/WEB-INF/lib下的jar包,解析其中的web-fragment.xml
    Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);

    // Step 2. Order the fragments.
    // 对多个地方出现的web.xml文件进行排序
    Set<WebXml> orderedFragments = null;
    orderedFragments =
            WebXml.orderWebFragments(webXml, fragments, sContext);

    // Step 3. Look for ServletContainerInitializer implementations
    // 通过spi来找到ServletContainerInitializer的实现
    if (ok) {
        processServletContainerInitializers();
    }

    if  (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
        // Steps 4 & 5.
        // 解析/WEB-INF/classes下面的类的注解
        processClasses(webXml, orderedFragments);
    }

    if (!webXml.isMetadataComplete()) {
        // Step 6. Merge web-fragment.xml files into the main 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
        if (ok) {
            convertJsps(webXml);
        }

        // Step 9. Apply merged web.xml to Context
        if (ok) {
            configureContext(webXml);
        }
    } else {
    	// 一般会走这个分支
    	// 合并配置
        webXml.merge(defaults);
        convertJsps(webXml);
        // 这里会生成wrapper对象
        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
    if (ok) {
        // Spec does not define an order.
        // Use ordered JARs followed by remaining JARs
        Set<WebXml> resourceJars = new LinkedHashSet<>(orderedFragments);
        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
    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());
            }
        }
    }
}

全局和host层级web.xml文件的解析

这里主要看下getDefaultWebXmlFragment这个方法
主要就是读取全局和host层级的web.xml文件,host层级的web.xml文件的配置优先级高于全局的,同时将配置文件解析结果加入缓存

private WebXml getDefaultWebXmlFragment(WebXmlParser webXmlParser) {

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

   // 从缓存中获取默认的webxml解析结果
   DefaultWebXmlCacheEntry entry = hostWebXmlCache.get(host);
   // 全局web.xml文件  ${tomcat目录}/resource/conf/web.xml文件
   InputSource globalWebXml = getGlobalWebXmlSource();
   // 当前host的web.xml ${tomcat目录}/resource/conf/Catalina/localhost/web.xml.default
   InputSource hostWebXml = getHostWebXmlSource();

   long globalTimeStamp = 0;
   long hostTimeStamp = 0;

   // 这里获取全局web.xml文件上一次修改的时间
   if (globalWebXml != null) {
       URLConnection uc = null;
       try {
           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;
               }
           }
       }
   }

   // 这里获取host层面的web.xml文件上一次修改的时间
   if (hostWebXml != null) {
       URLConnection uc = null;
       try {
           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;
               }
           }
       }
   }

   // 这里会判断之前的缓存是否有效,自从上次缓存之后,默认配置文件没有修改
   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
   // 这里对host的pipeline进行上锁
   synchronized (host.getPipeline()) {
       // 再次判断缓存
       entry = hostWebXmlCache.get(host);
       if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
               entry.getHostTimeStamp() == hostTimeStamp) {
           return entry.getWebXml();
       }

       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
       // 开始解析全局的web.xml文件,将解析结果村粗到webXmlDefaultFragment中
       if (globalWebXml == null) {
           // This is unusual enough to log
           log.info(sm.getString("contextConfig.defaultMissing"));
       } else {
           if (!webXmlParser.parseWebXml(
                   globalWebXml, webXmlDefaultFragment, false)) {
               ok = false;
           }
       }

       // Parse host level web.xml if present
       // Additive apart from welcome pages
       webXmlDefaultFragment.setReplaceWelcomeFiles(true);
	   // 解析host层级的web.xml文件
       if (!webXmlParser.parseWebXml(
               hostWebXml, webXmlDefaultFragment, false)) {
           ok = false;
       }

       // Don't update the cache if an error occurs
       // 更新默认web.xml文件的解析结果
       if (globalTimeStamp != -1 && hostTimeStamp != -1) {
           entry = new DefaultWebXmlCacheEntry(webXmlDefaultFragment,
                   globalTimeStamp, hostTimeStamp);
           hostWebXmlCache.put(host, entry);
           // Add a Lifecycle listener to the Host that will remove it from
           // the hostWebXmlCache once the Host is destroyed
           host.addLifecycleListener(new HostWebXmlCacheCleaner());
       }

       return webXmlDefaultFragment;
   }
}

解析context层级的web.xml的过程比较简单,就不分析了

解析应用引入的jar中的配置文件

protected Map<String,WebXml> processJarsForWebFragments(WebXml application,
            WebXmlParser webXmlParser) {

   JarScanner jarScanner = context.getJarScanner();
   boolean delegate = false;
   if (context instanceof StandardContext) {
       delegate = ((StandardContext) context).getDelegate();
   }
   boolean parseRequired = true;
   Set<String> absoluteOrder = application.getAbsoluteOrdering();
   if (absoluteOrder != null && absoluteOrder.isEmpty() &&
           !context.getXmlValidation()) {
       // Skip parsing when there is an empty absolute ordering and
       // validation is not enabled
       parseRequired = false;
   }

   FragmentJarScannerCallback callback =
           new FragmentJarScannerCallback(webXmlParser, delegate, parseRequired);

   // 重点在这里
   jarScanner.scan(JarScanType.PLUGGABILITY,
           context.getServletContext(), callback);

   if (!callback.isOk()) {
       ok = false;
   }
   return callback.getFragments();
}

看下StandardJarScanner的scan方法
主要会扫描三个地方的web-fragment.xml文件:WEB-INF/lib下、WEB-INF/classes(StandardJarScanner不会扫描这里)以及类路径下的jar

public void scan(JarScanType scanType, ServletContext context,
            JarScannerCallback callback) {

    if (log.isTraceEnabled()) {
        log.trace(sm.getString("jarScan.webinflibStart"));
    }

    if (jarScanFilter instanceof StandardJarScanFilter) {
        if (((StandardJarScanFilter) jarScanFilter).isSkipAll())
            return;
    }

    Set<URL> processedURLs = new HashSet<>();

    // Scan WEB-INF/lib
    Set<String> dirList = context.getResourcePaths(Constants.WEB_INF_LIB);
    if (dirList != null) {
        for (String path : dirList) {
            if (path.endsWith(Constants.JAR_EXT) &&
                    getJarScanFilter().check(scanType,
                            path.substring(path.lastIndexOf('/')+1))) {
                // Need to scan this JAR
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("jarScan.webinflibJarScan", path));
                }
                URL url = null;
                try {
                    url = context.getResource(path);
                    processedURLs.add(url);
                    // 调用callback的scan进行处理
                    process(scanType, callback, url, path, true, null);
                } catch (IOException e) {
                    log.warn(sm.getString("jarScan.webinflibFail", url), e);
                }
            } else {
                if (log.isTraceEnabled()) {
                    log.trace(sm.getString("jarScan.webinflibJarNoScan", path));
                }
            }
        }
    }

    // Scan WEB-INF/classes
    try {
        URL webInfURL = context.getResource(Constants.WEB_INF_CLASSES);
        if (webInfURL != null) {
            // WEB-INF/classes will also be included in the URLs returned
            // by the web application class loader so ensure the class path
            // scanning below does not re-scan this location.
            processedURLs.add(webInfURL);

            if (isScanAllDirectories()) {
                URL url = context.getResource(Constants.WEB_INF_CLASSES + "/META-INF");
                if (url != null) {
                    try {
                        callback.scanWebInfClasses();
                    } catch (IOException e) {
                        log.warn(sm.getString("jarScan.webinfclassesFail"), e);
                    }
                }
            }
        }
    } catch (MalformedURLException e) {
        // Ignore. Won't happen. URLs are of the correct form.
    }

    // Scan the classpath
    if (isScanClassPath()) {
        doScanClassPath(scanType, context, callback, processedURLs);
    }
}

看下FragmentJarScannerCallback的scan

public void scan(Jar jar, String webappPath, boolean isWebapp) throws IOException {

   InputStream is = null;
   WebXml fragment = new WebXml();
   fragment.setWebappJar(isWebapp);
   fragment.setDelegate(delegate);

   try {
       // Only web application JARs are checked for web-fragment.xml
       // files.
       // web-fragment.xml files don't need to be parsed if they are never
       // going to be used.
       // 扫描当前jar包下的META-INF/web-fragment.xml文件
       if (isWebapp && parseRequired) {
           is = jar.getInputStream(FRAGMENT_LOCATION);
       }

       if (is == null) {
           // If there is no web.xml, normal JAR no impact on
           // distributable
           fragment.setDistributable(true);
       } else {
           String fragmentUrl = jar.getURL(FRAGMENT_LOCATION);
           InputSource source = new InputSource(fragmentUrl);
           source.setByteStream(is);
           if (!webXmlParser.parseWebXml(source, fragment, true)) {
               ok = false;
           }
       }
   } finally {
   	   // 将解析结果放到fragments这个map中
       addFragment(fragment, jar.getJarFileURL());
   }
}

将配置应用到context

ContextConfig的configContext中从解析的配置结果中,将各种配置set到context中,这里主要看下如何处理servlet的

private void configureContext(WebXml webxml) {
	for (ServletDef servlet : webxml.getServlets().values()) {
			// 会创建一个StandardWrapper,并且将各种监听器设置上去
            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
			// 设置当前wrapper的属性
            if (servlet.getLoadOnStartup() != null) {
                wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
            }
            if (servlet.getEnabled() != null) {
                wrapper.setEnabled(servlet.getEnabled().booleanValue());
            }
            wrapper.setName(servlet.getServletName());
            // servlet配置的参数
            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());
            // servlet multipart部分的设置
            MultipartDef multipartdef = servlet.getMultipartDef();
            if (multipartdef != null) {
                long maxFileSize = -1;
                long maxRequestSize = -1;
                int fileSizeThreshold = 0;

                if(null != multipartdef.getMaxFileSize()) {
                    maxFileSize = Long.parseLong(multipartdef.getMaxFileSize());
                }
                if(null != multipartdef.getMaxRequestSize()) {
                    maxRequestSize = Long.parseLong(multipartdef.getMaxRequestSize());
                }
                if(null != multipartdef.getFileSizeThreshold()) {
                    fileSizeThreshold = Integer.parseInt(multipartdef.getFileSizeThreshold());
                }

                wrapper.setMultipartConfigElement(new MultipartConfigElement(
                        multipartdef.getLocation(),
                        maxFileSize,
                        maxRequestSize,
                        fileSizeThreshold));
            }
            // 是否支持异步
            if (servlet.getAsyncSupported() != null) {
                wrapper.setAsyncSupported(
                        servlet.getAsyncSupported().booleanValue());
            }
            wrapper.setOverridable(servlet.isOverridable());
            // 将当前wrapper作为context的子容器
            // 在将wrapper作为context的子容器的时候,会调用wrapper的start方法,start方法中会判断当前wrapper的状态为new,所以会先执行init方法,再执行start方法
            // StandardWrapper并没有实现initInternal方法,因此没有什么特殊的逻辑
            // startInternal也没什么特殊的逻辑
            context.addChild(wrapper);
        }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值