来源:http://diecui1202.iteye.com/blog/1037370
Context代表一个Web应用,它运行在某个指定的虚拟主机(Host)上;每个Web应用都是一个WAR文件,或是一个包含WAR解压后的文件的目录;
Connector组件接收到http请求后,通过将请求URI的最长可能前缀与每个Context的path进行匹配,然后选择相应的Web应用来处理这个http请求。之后,Context会根据web application deployment descriptor文件中定义的servlet映射,会选择一个正确的Servlet来处理请求。Servlet映射必须定义在该Web应用目录层次结构中的/WEB-INF/web.xml中。
一、几个重要的概念
1、context frgament file
Context片断文件,即描述Context配置的xml文件;它有三个层级:
a) Engine Scoped:适用于Engine下的所有Web应用程序,位于$CATALINA_BASE/conf/context.xml;
b) Host Scoped:适用于指定的Host组件下的应用程序,位于$CATALINA_BASE/[Engine]/[Host]/context.xml.default;
c) Web Application Scoped:代表一个独立的应用程序,它可以位于$CATALINA_BASE/[Engine]/[Host]/[ContextPath].xml或应用程序中的META-INF/context.xml;
对于应用程序中内嵌的context.xml文件,有两个局限性:
1) 当server.xml文件中的Host节点指定deployXML属性为false时,则会忽略Web应用程序内嵌的context.xml;
2) 内嵌的context.xml不能指定context path;
2、appBase、docBase&context path
a) appBase:Host组件中需要部署的应用的目录,如webapps;也可以指定绝对路径,将Web应用程序放在tomcat之外;
b) docBase:应用程序资源所有的路径;应用程序的资源包括HTML、CSS、JS、jar、class文件等,其中静态资源直接放在该docBase或其子目录下,jar文件放在WEB-INF/lib目录下,class文件则放在WEB-INF/classes目录下;
c) context path:唯一代表一个应用;
二、如何配置Context?
配置context有多种方式,如下:
1、将应用文件夹或war文件直接copy到tomcat的webapps目录下,这样tomcat启动的时候会将webapps目录下的文件夹或war文件的内容当成应用部署。这种方式最简单且无须书写任何配置文件。
2、在tomcat的server.xml配置文件中的Host节点下增加Context子节点,如:
- <Context path="/test" docBase="D:private omcat est.war" />
<Context path="/test" docBase="D:private omcat est.war" />其中,path即context path;docBase指向应用所在的文件夹或war文件,可以是绝对路径,也可以是相对路径(相对该Context所在的Host的appBase属性值);
3、在tomcat的conf/[Engine]/[Host]目录下新建xml文件,文件名为context path,内容如下:
- <Context docBase="D:private omcat est.war"
- privileged="true" antiResourceLocking="false" antiJARLocking="false">
- <!-- Link to the user database we will get roles from -->
- <ResourceLink name="users" global="UserDatabase"
- type="org.apache.catalina.UserDatabase"/>
- </Context>
<Context docBase="D:private omcat est.war" privileged="true" antiResourceLocking="false" antiJARLocking="false"> <!-- Link to the user database we will get roles from --> <ResourceLink name="users" global="UserDatabase" type="org.apache.catalina.UserDatabase"/> </Context>
其中,docBase与第二种方式中的含义一样;
当Host的autoDeploy属性值为true时,以上三种配置Context的方式中,只有第1、3两种方式配置署的应用不需要重启tomcat即可完成部署;第二种方式需要重启tomcat;另外,第1种方式不能指定特定的context path;
三、启动Context
在《Tomcat Host组件》这篇文档中提到,应用的部署是由HostConfig完成的;Host组件在初始化过程中会扫描三种不同类型的应用:context.xml描述文件、war包、文件夹;对于直接通过修改server.xml文件来配置的应用(上一节中的第2种场景),则是在启动tomcat解析server.xml文件时根据Context子节点来构造相应的应用;
不管是哪种方式配置Context(context.xml描述文件、war包、文件夹、配置server.xml的Context节点),最终都是构造出一个StandardContext实例,并将其作为子容器添加到Host组件中;与Host组件类似,在构造StandardContext实例时,会为每个StandardContext对象构造一个ContextConfig实例,该ContextConfig实例实现了LifecycleListener接口,它会监听StandardContext实例的各个生命周期事件,并针对不同的事件做出不同的响应;
当StandardContext实例以子容器被添加到Host组件中时,会触发StandardContext实例的start()方法,至此Context工作由此展开:
1、Context初始化和启动
Context初始化时,主要会触发init事件,并通过ContextConfig来完成初始化工作;
Context启动时主要完成如下事情:
a) 触发before_start事件来处理文件锁的问题(见下一小节before_start事件);
b) 根据docBase是war包还是文件夹,初始化应用程序资源的访问对象;
c) 初始化context特定的classloader:WebappLoader;
d) 计算work directory并初始化ServletContext:tomcat会为每个Context生成一个work directory,位于$CATALINA_BASE/work/[Engine]/[Host]目录下,work directory目录名根据context path而来(将“/”转换为“_”);work directory目录用于存放由jsp生成的servlet的class文件;
e) 启动内嵌组件,如Loader等;
f) 触发start事件来解析web应用描述符文件:如Servlet配置、Filter配置、Listener配置、session-timeout、welcome-file以及servlet参数等;
g) 启动Listerer;
h) 根据backgroundProcessorDelay决定是否需要开启后台线程:该后台线程可以用于管理session超时、监听类的变化来决定是否需要重启Context;当Context的reloadable属性为true时,如果应用程序的资源发生变化时,会通过backgroundProcess()来完成应用的重启;
i) 根据Servlet配置的loadOnStartup参数及大小顺序决定是否需要初始化Servlet及其初始化顺序;
2、ContextConfig事件处理
a) init事件
ContextConfig在处理init事件时,主要工作是:
1) 实例化了两个Digester类,分别为webDigester和contextDigester;webDigester用于解析web.xml配置文件,contextDigester用于解析前面提到的三个层级的context fragment file;在解析context fragment file时,主要的工作是解析内嵌元素,如Listener, Loader, Manager, Parameter, Resources, Valve以及WatchedResource,并将它们设置到该Context对应的属性上;;
2) 调整docBase属性值(即应用程序资源所在的位置):
- 在配置Context时如果没有指定docBase,则根据context path计算得出(“/”会替换成“#”,空会替换成ROOT),并转化为绝对路径;
- 根据Host中配置的unpackWARs属性,决定是否将配置在tomcat之外的war包解压到appBase下,并以context path为目录名(“/”会替换成“#”);此时docBase指向解压后的文件夹;如果的unpackWARs为false,则不会自动解压,此时docBase不变;
用流程图来表述如下:
b) before_start事件
在该过程中,主要是针对反文件锁做了一定的处理;在windows下,当ClassLoader加载类时,会打开相应的jar包,此时该jar包会被锁定,是不能被删除的;在tomcat中,当Context的antiResourceLocking设置为true时,如果删掉应用程序的war包时,对应的文件夹也会被删除,这是怎么做到的呢?
很简单,把应用程序的资源拷贝到一个临时的目录,然后将docBase指向该临时目录,这样锁定的都是副本;
c) start事件
当start事件发生时,ContextConfig会针对该Context完成如下工作:
1) 解析应用程序描述符:
a) $CATALINA_BASE/conf/web.xml:作用于所有的Web应用程序;
b) $CATALINA_BASE/conf/[Engine]/[Host]]/web.xml.default:作用于指定Host下的所有Web应用程序;
c) WEB-INF/web.xml:仅作用于当前Web应用程序;
2) 新版本中增加了对Listener、Filter、Servlet注解的支持;
3) 对web.xml里设置的一些roles进行一些简单的校验,,然后全部放到context持有的字符串数组securityRoles中;(这块没有仔细研究过,熟悉的同学可以解释一下);
4) 根据web.xml里配置的login-config等权限安全访问来确定是否为本context配置一个用于确定访问权限的Valve;如果需要,则通过addValve添加到pipeline中;
d) destroy事件
在Context需要reload时,会触发destory事件,此时则会将该Context的work directory目录删除;
e) stop事件
此时会清除所有与Context相关的配置,如子容器、从web.xml读取的配置信息(如Listener、Filter、Servlet等);
四、Context对请求的处理
Context在初始化时,会创建StandardContextValve的实例并加入pipeline中;Host组件在接收到请求后,会调用以下方法将请求转交给Context来处理:
- context.getPipeline().getFirst().invoke(request, response);
context.getPipeline().getFirst().invoke(request, response);
也就是会调用StandardContextValve的invoke方法,其具体流程如下:
1、先检查所请求的资源是否是保护资源,如果是,则直接返回客户端“资源不存在”(404);
2、如果应用在reload,则sleep 1s;此处是通过忙等待实现;同时用新的WebappLoader替换当前的ClassLoader;
3、如果找不到该请求对应的Wrapper(Servlet),则同样返回404;
4、触发ServletRequestListener的requestInitialized()方法;
5、将请求交由wrapper进行处理;
- wrapper.getPipeline().getFirst().invoke(request, response);
wrapper.getPipeline().getFirst().invoke(request, response);
6、wrapper处理完后,再触发发ServletRequestListener的requestDestroyed()方法;