前一篇文章讲了org.apache.catalina.startup.HostConfig
的 lifecycleEvent 方法中所做的事情。最后看到在 Tomcat 启动时或启动后(后台线程定时扫描)会调用 HostConfig 类的 deployApps 方法:

-startStop-
的线程还会运行一段时间才结束。但浏览这三种部署方式的实现代码,里面都是构建一个 Context 对象,并将构建好的 Context 对象与 Host 组件关联起来(即调用host.addChild(context)
这句,具体代码在 HostConfig 类的deployDescriptor(ContextName cn, File contextXml)
、deployDirectory(ContextName cn, File dir)
、deployWAR(ContextName cn, File war)
三个方法中,这里不再贴出代码来详细分析)。
前一篇文章只分析到这步,可以看出与一个 web 应用相对应的一个 Context 对象已经构建出来了,但如果容器只执行到这里根本无法响应一个浏览器的一次请求。就 web 服务器的实现来看一次请求过来除了需要根据内部 Context 构建找到这次请求访问的web应用具体所对应的 Context 对象,还需要包含 web 应用中具体的哪个 Servlet 来处理这次请求,中间是否还需要执行相应的过滤器( filter )、监听器( listener )等,做过 java 的 web 开发的同学都知道,这些信息是配置在一个 web 应用的WEB-INF\web.xml
文件的(servlet3 中已经支持将这些配置信息放到 Java 文件的注解中,但万变不离其宗,总归要在 web 应用的某个地方说明,并在容器启动时加载,这样才能真正提供 web 服务,响应请求)。
看到这里可以猜到 Tomcat 容器加载 web 应用时必定会有对于每个应用的 web.xml 文件的解析过程,本文就来看看这个解析过程。
在本文开头提到的三种部署应用的实现代码中有一些共通的代码,这里摘出来说明一下:
Class<?> clazz = Class.forName(host.getConfigClass());
LifecycleListener listener =
(LifecycleListener) clazz.newInstance();
context.addLifecycleListener(listener);
host.addChild(context);
第一段是在所有 Context 对象构建时会添加一个监听器,这里监听器的类名是 StandardHost 类的实例变量 configClass ,其默认值就是org.apache.catalina.startup.ContextConfig
。第二段是将当前构建的 Context 对象添加到父容器 Host 对象中。
先看 StandardHost 的 addChild 方法的实现:



即给 host 对象添加子容器时将会调用子容器的 start 方法,按照前面文章的分析,调用 StandardContext 的 start 方法最终会调用org.apache.catalina.core.StandardContext
类的 startInternal 方法(该方法代码较长,建议自己阅读,不再贴出),这里将会发布一系列事件,按调用前后顺序这些事件包括:BEFORE_INIT_EVENT
、AFTER_INIT_EVENT
、BEFORE_START_EVENT
、CONFIGURE_START_EVENT
、START_EVENT
、AFTER_START_EVENT
。
前面提到在构建 Context 对象时都会注册一个监听器org.apache.catalina.startup.ContextConfig
,看下这个类的 lifecycleEvent 方法中(为什么会执行这个方法可以看这篇文章的分析)监听了哪些事件:

AFTER_INIT_EVENT
(执行 init 方法)、BEFORE_START_EVENT
(执行 beforeStart 方法)、CONFIGURE_START_EVENT
(执行 configureStart 方法)。
在 configureStart 方法将直接调用 webConfig 方法,正是在这个方法中将会解析 web.xml 文件:






webXml.configureContext(context)
)。
看下 configureContext 方法:






