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