浅析Tomcat之StandardContext

Context是Host的子容器,在servlet引擎中它代表了一个web application.它在每一个Catalina中部署的应用几乎都是存在的.它的子容器是Wrapper(一个具体servlet的定义),Context是标准实现是StandardContext,与StandardHost的实现模式类似.它承担了创建Wrapper容器(Servlet),Filter,ErrorPage等在web.xml中配置的内容.

首先构造函数总也是给pipeline添加一个StandardContextValve的基础阀.这个阀与前面介绍的2种不同的是它对用户访问的路径进行了限制.且看其invoke方法.

public final void invoke(Request request, Response response)  
    throws IOException, ServletException {  
  
    // Disallow any direct access to resources under WEB-INF or META-INF  
    MessageBytes requestPathMB = request.getRequestPathMB();  
    if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))  
            || (requestPathMB.equalsIgnoreCase("/META-INF"))  
            || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))  
            || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {  
        response.sendError(HttpServletResponse.SC_NOT_FOUND);  
        return;  
    }  
  
    // Select the Wrapper to be used for this Request  
    Wrapper wrapper = request.getWrapper();  
    if (wrapper == null || wrapper.isUnavailable()) {  
        response.sendError(HttpServletResponse.SC_NOT_FOUND);  
        return;  
    }  
  
    //....(此处省略部分代码)  
  
    wrapper.getPipeline().getFirst().invoke(request, response);  
}

首先它会限制用户查询META-INF和WEB-INF中的内容,这也就是Tomcat中为何在url中无法访问这些路径中的内容的原因所在.接着它会找到具体的Wrapper,而调用其阀的方法.也就是把调用转发到具体的servlet了.

知道了Context的请求转发方式,下面看看它是如何创建子容器的,与HostConfig类似,有个ContextConfig类来侦听Conext的生命周期事件.对于StandardContext 所派发出的生命周期事件.lifecycleEvent根据不同侦听的事件类型来处理事件.其中的AFTER_INIT_EVENT事件 调用init方法,解析Context.xml,CONFIGURE_START_EVENT事件调用了configStart方法来初始化Context的一些配置,其中它调用了一个我们比较关心的方法是webConfig()函数,读取web.xml并且解析和创建了Context的子容器Wrapper,也就是Servlet.

Set<WebXml> defaults = new HashSet<WebXml>();
defaults.add(getDefaultWebXmlFragment());

WebXml webXml = createWebXml();

// Parse context level web.xml
InputSource contextWebXml = getContextWebXmlSource();
parseWebXml(contextWebXml, webXml, false);

ServletContext sContext = context.getServletContext();

// Ordering is important here

// Step 1. Identify all the JARs packaged with the application
// If the JARs have a web-fragment.xml it will be parsed at this
// point.
Map<String,WebXml> fragments = processJarsForWebFragments();

// Step 2. Order the fragments.
Set<WebXml> orderedFragments = null;
orderedFragments = WebXml.orderWebFragments(webXml, fragments);

// Step 3. Look for ServletContainerInitializer implementations
if (ok) {
	processServletContainerInitializers(orderedFragments);
}

if  (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
	// Step 4. Process /WEB-INF/classes for annotations
	if (ok) {
		......
	}

	// Step 5. Process JARs for annotations - only need to process
	// those fragments we are going to use
	if (ok) {
		processAnnotations(
				orderedFragments, webXml.isMetadataComplete());
	}

	// 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
	// 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) {
		webXml.configureContext(context);
	}
} else {
	webXml.merge(defaults);
	convertJsps(webXml);
	webXml.configureContext(context);
}

// Step 9a. Make the merged web.xml available to other
// components, specifically Jasper, to save those components
// from having to re-generate it.
// TODO Use a ServletContainerInitializer for Jasper
String mergedWebXml = webXml.toXml();
sContext.setAttribute(
	   org.apache.tomcat.util.scan.Constants.MERGED_WEB_XML,
	   mergedWebXml);
if (context.getLogEffectiveWebXml()) {
	log.info("web.xml:\n" + mergedWebXml);
}

// Always need to look for static resources
// Step 10. Look for static resources packaged in JARs
if (ok) {
	.....
}

// Step 11. Apply the ServletContainerInitializer config to the
// context
if (ok) {
	.....
}

上述代码是webconfig函数,getDefaultWebXmlFragment取出了上层容器中的web.xml和web-fragment.xml这些是基础的配置.接着getContextWebXmlSource读取的是Context下面的web.xml也就是WEB-INF/web.xml,而parseWebXml对它进行了解析.所使用的是digester,具体的规则是WebRuleSet(有兴趣的可以仔细阅读),在此只是将解析好的web.xml转化为java形式的配置还没有进行Servlet实质上的创建和部署. processJarsForWebFragments把/WEB-INF/lib文件夹下的jar包读取一遍,查找其中的/META-INF/web-fragment.xml而WebXml.orderWebFragments(webXml, fragments)把其中需要进行执行的进行顺序排序.后面的步骤是比较多的,读者可自行阅读.这里重点看下step6前面的判断条件!webXml.isMetadataComplete()不成立的时候执行的是配置文件的合并.然后调用了webXml.configContext.此方法在实质上是执行Context配置文件中所配置的内容,也就是Wrapper子容器等的创建.这里主要看下Wrapper的配置内容.

for (ServletDef servlet : servlets.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 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);  
}  

Servlet在配置文件解析后体现为ServletDef 类,里面存放了Servlet诸多属性.创建是通过wrapper来进行封装的.我们可以看到很都Servlet的属性都设置到其中,最后是context.addChild(wrapper)把创建好的wrapper作为context的子容器加入其中.configContext除了创建wrapper等还承担了创建Filter,ErrorPage等在web.xml中配置的内容.



留个爪印