Web应用加载属于Server启动的核心处理过程。Catalina对Web应用的加载主要由StandardHost、HostConfig、StandardContext、ContextConfig、StandardWrapper5个类完成。
1.1StandardHost
StandardHost的启动加载过程如下:
(1)为Host添加一个Valve实现ErrorReportValve,该类主要用于在服务器处理异常时输出错误页面。如果没有在web.xml中添加错误处理页面,Tomcat返回的异常页面则是由ErrorReportValve生成。
注意:如果希望制定Web应用的错误页面,除了按照Servlet规范在web.xml中添加外,还可以通过设置Host的ErrorReportValve属性实现。前者的作用范围是当前Web应用,后者是整个虚拟机。修改该配置的一个重要原因是,出于安全考虑隐藏服务器细节,毕竟ErrorReportValve输出的内容包含了服务器信息。
(2)调用StandardHost父类ContainerBase的startInternal()方法启动虚拟机,其处理分如下几步。
  a、如果配置了集群组件Cluster,则启动。
  b、如果配置了安全组件Realm,则启动。
  c、启动子节点(即通过server.xml中的<Context>创建的StandartContext实例)。
  d、启动Host持有的Pipeline组件。
  e、设置Host状态为starting,此时会触发START_EVEN生命周期事件。HostConfig监听该事件,扫描Web部署目录。对部署扫描文件、WAR包、目录会自动创建StandardContext实例,添加到Host并启动。
f、启动Host层级的后台任务处理:Cluster后台任务处理、Realm后台任务处理、Pipeline中Valve后台任务处理。
1.2HostConfig
在大多数情况下,Web应用部署并不需要配置多个基础目录,而是能够做到自动、灵活部署,这也是Tomcat的默认部署方式。
在默认情况下,server.xml并未包含Context相关配置,仅包含Host配置,如下:
<Host name="losthost" appBase="webapps" unpackWARs="true" autoDeploy="true"></Host>
其中,appBase为Web应用部署的基础目录,所有需要部署的Web应用均需要复制到此目录下,默认为$CATANLINA_BASE/webapps。Tomcat通过HostConfig完成该目录下Web应用的自动部署。
HostConfig处理生命周期事件包括:START_EVEN、PERIODIC_EVEN、STOP_EVEN。其中,前两者都与Web应用部署密切相关,后者用于在Host停止时注销其对应的MBean。
1.START_EVEN事件
该事件在Host启动时触发,完成服务器启动过程中的Web应用部署。该事件包含了3个部分:Context描述文件部署、Web目录部署、WAR包部署,这3部分对应于Web应用的3类不同的部署方式。
- Context描述文件部署
Tomcat支持通过一个独立的Context描述文件来配置并启动Web应用,配置方式同server.xml中的元素。该配置文件的存储路径由Host的xmlBase属性指定。如未指定,则默认为$CATALINA_BASE/conf/<Engine名称>/<Host名称>。
例如在该目录下建立一个文件“myApp.xml”,内容如下:
<Context docBase="test/myApp" path="/myApp" reloadable="false">
<WatchedResource>WEB_INF/web.xml</WatchedResource>
</Context>
与此同时,将目录名为myAPP的Web应用复制到test目录下,Tomcat启动时会自动部署该Web应用。此方式与在server.xml中的配置相比要更加灵活,而且可以实现相同的部署需求。
Context描述文件的部署过程如下:
(1)扫描Host配置文件基础目录,即 $CATALINA_BASE/conf/<Engine名称>/<Host名称>,对于该目录下的每个配置文件,由线程池完成解析部署。
(2)对于每个文件的线程部署,进行如下操作。
a.使用Digester解析配置文件,创建Context实例。
b.更新Context实例的名称、路径,此时元素中的path属性无效。
c.为Context添加ContextConfig生命周期监听器。
d.通过Host的addChild()方法将Context实例添加到Host。该方法会判断Host是否已启动,如是,则直接穹顶Context。
e.将Context描述文件、Web应用目录及web.xml添加到守护资源,以便文件发生变更时,重新部署或加载Web应用。
- Web目录部署
以目录的形式发布并部署Web应用是Tomcat中最常见的部署方式。只需要将包含Web应用所有资源文件、Jar包、描述文件的目录复制到Host指定appBase目录下即可完成部署。
此种部署方式下,Catalina同样支持通过配置文件来实例化Context,但无法覆盖name、path、webappVersion、docBase这4个属性,这些均由Web目录的路径及名称确定。
Catalina部署Web应用目录的过程如下。
(1)对于Host的deploy的appBase目录(默认为$CATALINA_BASE/webapps)下所有符合条件的目录,由线程池完成部署。
(2)对于每个目录进行如下操作。
a.如果Host的deployXML属性值为true,并且存在META-INF/context.xml文件,则使用Digester解析context.xml文件创建Context对象。如果Context的copyXML属性为true,则将描述文件复制到 $CATALINA_BASE/conf/<Engine名称>/<Host名称>目录下,文件名与Web应用目录名相同。
如果deployXML属性值为false,但存在META-INF/context.xml文件,则构造FailedContext实例。
其他情况下,根据Host的contextClass属性指定的类型创建Context对象。如不指定,则为org.apache.catalina.core.StandardContext。此时,所有的Context属性均采用默认配置,除name、path、webappVersion、docBase会根据Web应用目录的路径及名称进行配置外。
b.为Context实例添加ContextConfig生命周期监听器。
c.通过Host的addChild()方法将Context实例添加到Host。该方法会判断Host是否已启动,如果是,则直接启动Context。
d.将Context描述文件、Web应用目录及web.xml添加到守护资源,以便文件发生变化时重新部署或加载Web应用。 - WAR包部署
WAR包部署和Web目录部署类似,只是由于WAR包作为一个压缩文件,增加了部分针对压缩的处理。
具体部署过程如下。
(1)对于Host的appBase目录下所有符合条件的WAR包,由线程池完成部署。
(2)对于每个WAR包进行如下操作。
a.如果Host的deploy属性值为true,且在WAR包同名目录下存在META-INF/context.xml文件,同时Context的copyXML属性为false,则使用该描述文件创建Context实例。
如果Host的deploy属性值为true,且在WAR包压缩文件下存在META-INF/context.xml文件,则使用该描述文件创建Context对象。
b如果deployXML属性值为false,但是在WAR包压缩文件下存在META-INF/context.xml文件,则构造FailedContext实例。
b.如果deployXML为true,且META-INF/context.xml存在于WAR包中,同时Context的copyXML属性为true,则将context.xml文件复制到 $CATALINA_BASE/conf/<Engine名称>/<Host名称>目录下,文件名同WAR包名称。
c.为Context实例添加ContextConfig生命周期监听器。
d.通过Host的addChild()方法将Context实例添加到Host。该方法会判断Host是否已启动,如果是,则直接启动Context。
e将Context描述文件、Web应用目录及web.xml添加到守护资源,以便文件发生变化时重新部署或加载Web应用。
2.PERIODIC_EVEN事件
Catalina的容器支持定期执行自身及子容器的后台处理过程,该机制常用于定时扫描Web应用的变更,并进行重新加载。后台任务处理完成后,将触发PERIODIC_EVEN事件。
在HostConfig中通过DeployedApplication维护了两个守护资源列表:redeployResource和reloadResource,前者用于守护导致应用重新部署的资源,后者守护导致应用重新加载的资源。当HostConfig接收到PERIODIC_EVEN事件后,会检测守护资源的变更情况。如果发生变更,将重新加载或部署应用以及更新资源的最后修改时间。
(1)对于每一个已部署的Web应用,检查用于重新部署的守护资源,对于每一个守护的资源文件或目录,如果发生变更,则有以下情况。
a.如果资源对应为目录,则仅更新守护资源列表中的上次修改时间。
b.如果Web应用存在Context描述文件并且当前变更的是WAR包,则得到原Context的docBase。如果docBase不以“.war”结尾,删除解压目录并重新加载,否则直接重新加载。更新守护资源。
c.其他情况,直接卸载应用,并由后面的处理重新部署。
(2)对于每个已部署的Web应用,检查用于重新加载的守护资源,如果资源发生变更,则重新加载Context对象。
(3)如果Host配置为卸载旧版本应用,则检查并卸载。
(4)部署Web应用,部署过程同上。
无论是Context描述文件,还是Web目录以及WAR包,归结起来,Catalina支持Web应用以文件目录或WAR包的形式发布;同时,如果希望定制Context,那么可以通过$CATALINA_BASE/conf/<Engine名称>/<Host名称>目录下的描述文件或Web应用的META-INF/context.xml来进行定义。因此,从这个角度,可以将Catalina的Web应用部署分为目录和WAR包两类,每一类进一步支持Context的定制化。而默认情况下,Catalina会根据发布包的路径及名称自动创建一个Context对象。