|
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中配置的内容.
首发于泛泛之辈 - http://www.lihongkun.com/archives/154 |
|