前言
由于换工作的原因,需要融入新的开发团队,开展新的业务征途,因此,距离上一次更新博客已有一段时间,现在稍微稳定下来可以继续Tomcat源码的分析。在回顾思路时发现,之前对于Tomcat组件和生命周期的文章主要从宏观角度分析了两者执行的大体流程,对于细节点的分析有所欠缺,而这些细节可能又是后期理解 “Tomcat处理请求响应” 这种重量级流程的前提。为了更好的温故而知新,未来将对Tomcat架构中各个组件及组件间关系和Tomcat的生命周期两篇文章进行更深程度的剖析,本文是其中的第一部分,主要强化的内容如下:
- Digester解析xml文件模式的详细分析,结合之前的文章,读者会了解到Tomcat中涉及到的所有关键解析规则和原理
- 其他Connector、Container相关组件解析的详细过程。在之前的文章中,仅以
顶层标签举例,并不涉及
、
、
等“子标签”,而Tomcat标签的解析越往“子标签” 越复杂,越接近Tomcat处理请求响应的核心,因此为了进一步的深入也需要将“子标签”的解析吃透
在Tomcat架构中各个组件及组件间关系中,我们已对Digester类工作的大体思路进行了分析,并且以
标签的解析进行了举例,
是
的子标签,对应的Digester解析规则如下
addObjectCreate(String pattern, String className, String attributeName)
底层使用的规则为
ObjectCreateRule
,方法的第一个参数是pattern,表明了解析到什么标签才会使用配置的规则对标签的内容进行解析,和正则表达式匹配的作用类似。比如上图中的pattern为
Server/Service
表示解析到
下的
标签时运用规则进行解析,这里用
/
表示一种父子关系。 第二个参数
className
很明显表示标签对应的java实体类,从上图中来说
标签对应的实体实际上就是
StandardService
,其实该参数是一个可选参数,可以传null,用第三个参数
attributeName
在运行时指定该标签对应的类,以图中举例就是说
标签可以存在一个属性,属性名为
className
,当第二个参数没有指定时,
Digester
会自动解析该属性,并通过反射生成该类的实例再压入
Digester
内部的栈顶。
addSetProperties(String pattern)
底层使用的规则为
SetPropertiesRule
,方法唯一的参数也是pattern,同样表示遇到何种标签才进行解析,
SetPropertiesRule
规则用于解析标签对应的属性。以上图举例,
标签如下所示
其属性只有name一个,那我们猜想在
StandardService
中可能存在一个该属性对应的set方法,看下
StandardService
的代码发现确实如此
这里有一个小坑需要说明一下,实际上标签对应的实体类并不一定存在标签属性对应的set方法,并且也不是存在对应属性的set方法就会调用,理解这个细节我们需要进入到
SetPropertiesRule
类的
begin()
方法中
红框处存在三个判断,第一个
digester.isFakeAtrribute(top, name)
,其中
top
是当前
Digester
内部栈中栈顶元素,对于
而言栈顶元素就是
StandardService
,
name
是每一个属性的名称,
isFakeAtrribute
具体的逻辑如下
该方法实际上就是在一个
Map>
的集合中判断某个类是否存在某个名称的属性,如果存在就返回true,进而不去调用该属性的set方法,那么有哪些属性被放在了这个“假属性”集合中呢?我们回头看
Catalina
中定义
server.xml
解析规则的方法
Tomcat在创建
Digester
类之前默认添加了key为
Object.class
的entry,其value为包含
className
的集合,结合图5代码的逻辑可知,如果标签没有设置特定的fake attributes,那么总会返回默认的,包含名称为
className
的集合。而正常情况下所有的标签都是没有设置特定的fake attributes的,也就是说
Digester
在解析所有标签时都会排除名称为
className
的属性
我们再来看图4判断中的第二个部分
IntrospectionUtils.setProperty(top, name, value)
,最终调用的逻辑如
代码清单1
public static boolean setProperty(Object o, String name, String value,
boolean invokeSetProperty) {
if (log.isDebugEnabled())
log.debug("IntrospectionUtils: setProperty(" +
o.getClass() + " " + name + "=" + value + ")");
String setter = "set" + capitalize(name);
try {
Method methods[] = findMethods(o.getClass());
Method setPropertyMethodVoid = null;
Method setPropertyMethodBool = null;
// First, the ideal case - a setFoo( String ) method
for (int i = 0; i < methods.length; i++) {
Class> paramT[] = methods[i].getParameterTypes();
if (setter.equals(methods[i].getName()) && paramT.length == 1
&& "java.lang.String".equals(paramT[0].getName())) {
methods[i].invoke(o, new Object[] { value });
return true;
}
}
// Try a setFoo ( int ) or ( boolean )
for (int i = 0; i < methods.length; i++) {
boolean ok = true;
if (setter.equals(methods[i].getName())
&& methods[i].getParameterTypes().length == 1) {
// match - find the type and invoke it
Class> paramType = methods[i].getParameterTypes()[0];
Object params[] = new Object[1];
// Try a setFoo ( int )
if ("java.lang.Integer".equals(paramType.getName())
|| "int".equals(paramType.getName())) {
try {
params[0] = Integer.valueOf(value);
} catch (NumberFormatException ex) {
ok = false;
}
// Try a setFoo ( long )
}else if ("java.lang.Long".equals(paramType.getName())
|| "long".equals(paramType.getName())) {
try {
params[0] = Long.valueOf(value);
} catch (NumberFormatException ex) {
ok = false;
}
// Try a setFoo ( boolean )
} else if ("java.lang.Boolean".equals(paramType.getName())
|| "boolean".equals(paramType.getName())) {
params[0] = Boolean.valueOf(value);
// Try a setFoo ( InetAddress )
} else if ("java.net.InetAddress".equals(paramType
.getName())) {
try {
params[0] = InetAddress.getByName(value);
} catch (UnknownHostException exc) {
if (log.isDebugEnabled())
log.debug("IntrospectionUtils: Unable to resolve host name:" + value);
ok = false;
}
// Unknown type
} else {
if (log.isDebugEnabled())
log.debug("IntrospectionUtils: Unknown type " +
paramType.getName());
}
if (ok) {
methods[i].invoke(o, params);
return true;
}
}
// save "setProperty" for later
if ("setProperty".equals(methods[i].getName())) {
if (methods[i].getReturnType()==Boolean.TYPE){
setPropertyMethodBool = methods[i];
}else {
setPropertyMethodVoid = methods[i];
}
}
}
// Ok, no setXXX found, try a setProperty("name", "value")
if (invokeSetProperty && (setPropertyMethodBool != null ||
setPropertyMethodVoid != null)) {
Object params[] = new Object[2];
params[0] = name;
params[1] = value;
if (setPropertyMethodBool != null) {
try {
return ((Boolean) setPropertyMethodBool.invoke(o,
params)).booleanValue();
}catch (IllegalArgumentException biae) {
//the boolean method had the wrong
//parameter types. lets try the other
if (setPropertyMethodVoid!=null) {
setPropertyMethodVoid.invoke(o, params);
return true;
}else {
throw biae;
}
}
} else {
setPropertyMethodVoid.invoke(o, params);
return true;
}
}
} catch (IllegalArgumentException ex2) {
log.warn("IAE " + o + " " + name + " " + value, ex2);
} catch (SecurityException ex1) {
log.warn("IntrospectionUtils: SecurityException for " +
o.getClass() + " " + name + "=" + value + ")", ex1);
} catch (IllegalAccessException iae) {
log.warn("IntrospectionUtils: IllegalAccessException for " +
o.getClass() + " " + name + "=" + value + ")", iae);
} catch (InvocationTargetException ie) {
ExceptionUtils.handleThrowable(ie.getCause());
log.warn("IntrospectionUtils: InvocationTargetException for " +
o.getClass() + " " + name + "=" + value + ")", ie);
}
return false;
}
方法中将原始的方法名通过capitalize
进行首字母大写处理,最终加上set前缀赋给setter变量。之后会根据类的实例得到对象所有的方法,并与setter进行匹配,匹配成功则直接invoke调用并返回true,没有找到对应属性的set方法则返回false。正是这种处理解释了上面我们提及的:有些标签即便配置了相关的属性也不会调用对应类的set方法
接下来我们再看第三个解析规则addSetNext(String pattern, String methodName, String paramType)
,底层使用的规则为SetNextRule
,方法的第一个参数指明了触发该规则的具体模式,第二个参数表明调用父标签对应实体的方法名称,第三个参数就是方法参数的类型。在这里因为当前栈顶元素为StandardService
,addSetNext
会调用StandardServer
的addService(Service service)
方法,将当前StandardService
与其父元素StandardServer
建立关联,涉及相关代码如下
我们可以稍微总结一下
Digester
内置的三大解析规则类对应的用途
Rule | 对应方法 | 用途 |
---|---|---|
ObjectCreateRule |
addObjectCreate |
根据匹配解析模式创建对应标签的实体类 |
SetPropertiesRule |
addSetProperties |
根据匹配解析模式为对应标签实体类设置相关属性 |
SetNextRule |
addSetNext |
建立标签对应实体之间子父类关系 |
至此
标签的规则配置及解析流程分析完毕,我们接着看
标签
创建
对象的规则和
规则不太一样,并没有使用
Digester
內建的
ObjectCreateRule
,而是自己继承
Rule
创建了
ConnectorCreateRule
。前文中分析过,当解析到对应标签的开始处会调用规则类的
begin()
,我们来看看它做了什么
方法中首先取出此时栈顶元素
StandardService
(
Connector
尚未创建),再从包含所有
标签属性的
attributes
中查找是否存在exector属性,存在最终会调用
_setExecutor(Connector, Executor)
方法,该方法的主要作用是设置处理端到端连接的线程池,默认情况下
server.xml
中并不会事先设置该线程池,但即便不设置,之后在Tomcat启动时也会默认创建一个,在后面分析启动流程时会详细分析,这里暂先按默认未设置线程池流程走。之后会根据
protocol
属性创建
Connector
对象,基于 Tomcat架构中各个组件及组件间关系中给出的
server.xml
可知,
标签共有两种协议,一种是HTTP/1.1,另一种是AJP/1.3。前者大家很清楚是HTTP协议的1.1版本,后者一般用于web容器之间通信,比HTTP协议在web容器间拥有更高的吞吐量。因为存在两种协议,那就会存在两个
Connector
实体,为了突出重点,我们只分析最常用的HTTP协议对应的
Connector
初始化流程
Connector
构造器会继续调用
setProtocol(String protocol)
方法,并将协议对应的字符串传入
第一个判断涉及到一种apr请求处理方法,可以将其理解为一种高效的IO模式,底层基于JNI技术调用操作系统级别的IO接口,在默认情况下也是关闭的。因此上述代码最终会调用
setProtocolHandlerClassName("org.apache.coyote.http11.Http11Protocol")
,将
Connector
中的成员变量
ProtocolHandler
置为
Http11Protocol
,该类在处理请求响应的流程中起到了重要作用,后续文章会详细分析,这里记住即可
在处理属性是也添加了名为
SetAllPropertiesRule
的规则,该规则接收了一个排除属性的数组,其中仅包含
executor
属性
begin()
方法中不仅按上面提到的流程对属性进行了筛选,而且根据该规则中设置的排除属性数组再一次进行了过滤。同样的,在
addSetNext
方法中会调用父标签
StandardService
的
addConnector(Connector)
,从而建立父子关联关系
在方法中使用了
synchronized
代码块解决了并发访问下新增
Connector
被覆盖的问题,在 Tomcat的生命周期中说到,每一个容器都一个生命周期状态的概念,这里
getState()
就获得了此时
Connector
的状态,在刚创建时容器的state为
NEW
,
available
属性值为false,并不会立即启动
Connector
容器,至此
的解析过程也分析完毕
Tomcat从整体架构上可以分为两大部分:监听请求并生成对应Request和Response的
Connector
连接器,以及处理请求和控制tomcat容器运转的
Container
。
标签就是
Container
的顶层组件,每一个
Engine
相当于一个Servlet引擎,其下可以存在多个
Host
和
Context
子容器
乍一看貌似
相关的rule特别多,但仔细一看其实都是套路,按照上面的分析方式都能一一拿下,这里只说一些重点和不同的部分。规则中为
添加了一个名为
EngineConfig
的Listener,用于对
StandardEngine
组件的生命周期监控
从图中可以看到该类在事件为start和stop时会进行日志的打印,此外并没有进行其他的操作,在
StandardEngine
初始化时存在一个管道
Pipeline
和阀门
Valve
的概念
Tomcat中为了更高效的处理请求,内部设计了
Pipeline
和
Valve
的概念,相当于Servlet中的Filter和FilterChain,管道中可以通过
addValve(Valve)
添加或通过
removeValve(Valve)
移除多个阀门,而有一种阀门被称为基础阀门,该阀门总是最后一个执行的,比如这里的
StandardEngineValve
,关于两者的详细分析会在后续文章开展,这里不做累述。参数
backgroundProcessorDelay
和
ContainerBase
中的内部类
ContainerBackgroundProcessor
有关,该类实现了
Runnable
接口,用于检测war包中的类文件是否改动,是否需要重新加载,而参数乘以默认的基数就是执行的间隔时间,具体的处理流程后续文章同样会讲到。
setJvmRoute()
是给该机器设置一个唯一的标识,当有多台机器组成cluster时,每台机器都会用这唯一的标识代表自身在集群中的位置
回到
EngineRuleSet
的规则定义上,我们发现Tomcat还为每一个
StandardEngine
添加了
RealmRuleSet
,该规则对应
的子标签
,此标签引出了一个“域”的概念,我们可以将多个web应用划分成多个域,给每个域设定不同的访问权限,只有拥有对应域访问权限的角色才能访问对应的web应用,因此该规则的设定主要为了安全访问和权限管理
一个
表示一个虚拟主机,其下可以存在多个
标签,
标签对应的规则定义如下
对应的实体类为StandardHost
,在初始化时也给Host
容器中的管道添加了一个基础阀门StandardHostValve
。同StandardEngine
一样,Tomcat也为StandardHost
添加了一个监听器HostConfig
,但其功能远比EngineConfig
复杂很多
它根据不同的事件类型对web应用的进行相应的检查发布,停止以及和上面提到的
ContainerBackgroundProcessor
线程结合起来监控应用是否需要reload等功能,这部分内容和容器的生命周期关系更加紧密,且可讲的内容较多,将放在生命周期强化的第二部分讲解
一个
可以认为对应一个webapps下的目录,或者一个war包。代表虚拟主机的
下可以存在多个
标签,
对应的解析规则也是继承
RuleSetBase
创建了自己的规则集合
ContextRuleSet
标签对应的实体是
StandardContext
,也存在基础阀门
StandardContextValve
,添加了对应的监听器
ContextConfig
,在对该监听器进行说明之前不知道大家想过没有,到目前为止,我们一直在讨论
server.xml
文件的解析,那其他的xml文件,比如
context.xml
、
web.xml
是什么时候解析的呢?为了回答这一问题,我们来看一下
ContextConfig
是如何处理监听事件发生的
在 Tomcat的生命周期中曾给出Tomcat生命周期流转图和全部生命周期状态字段,结合上图两处红框中的Lifecycle类型可知,
Lifecycle.AFTER_INIT_EVENT
发生在
Lifecycle.CONFIGURE_START_EVENT
之前,而前者的
init()
中就定义了解析
web.xml
文件的所有规则
继续深入
WebRuleSet
同样继承了
RuleSet
,
web.xml
存在两种形式,一种是我们“通常”意义上,放在每一个war包内的
webapps/WEB-INF/web.xml
,该配置文件是以
作为根元素的;另一种是为了支持Servlet3.0新特性将
web.xml
分成多个小部分,运行时再将各个部分聚集起来解析的配置文件
web-fragment.xml
,该文件是以
作为根元素。
代码清单2为
WebRuleSet
设置的所有标签的解析规则
@Override
public void addRuleInstances(Digester digester) {
digester.addRule(fullPrefix,
new SetPublicIdRule("setPublicId"));
digester.addRule(fullPrefix,
new IgnoreAnnotationsRule());
digester.addRule(fullPrefix,
new VersionRule());
// Required for both fragments and non-fragments
digester.addRule(fullPrefix + "/absolute-ordering", absoluteOrdering);
digester.addRule(fullPrefix + "/ordering", relativeOrdering);
if (fragment) {
// web-fragment.xml
digester.addRule(fullPrefix + "/name", name);
digester.addCallMethod(fullPrefix + "/ordering/after/name",
"addAfterOrdering", 0);
digester.addCallMethod(fullPrefix + "/ordering/after/others",
"addAfterOrderingOthers");
digester.addCallMethod(fullPrefix + "/ordering/before/name",
"addBeforeOrdering", 0);
digester.addCallMethod(fullPrefix + "/ordering/before/others",
"addBeforeOrderingOthers");
} else {
// web.xml
digester.addCallMethod(fullPrefix + "/absolute-ordering/name",
"addAbsoluteOrdering", 0);
digester.addCallMethod(fullPrefix + "/absolute-ordering/others",
"addAbsoluteOrderingOthers");
}
digester.addCallMethod(fullPrefix + "/context-param",
"addContextParam", 2);
digester.addCallParam(fullPrefix + "/context-param/param-name", 0);
digester.addCallParam(fullPrefix + "/context-param/param-value", 1);
digester.addCallMethod(fullPrefix + "/display-name",
"setDisplayName", 0);
digester.addRule(fullPrefix + "/distributable",
new SetDistributableRule());
configureNamingRules(digester);
digester.addObjectCreate(fullPrefix + "/error-page",
"org.apache.catalina.deploy.ErrorPage");
digester.addSetNext(fullPrefix + "/error-page",
"addErrorPage",
"org.apache.catalina.deploy.ErrorPage");
digester.addCallMethod(fullPrefix + "/error-page/error-code",
"setErrorCode", 0);
digester.addCallMethod(fullPrefix + "/error-page/exception-type",
"setExceptionType", 0);
digester.addCallMethod(fullPrefix + "/error-page/location",
"setLocation", 0);
digester.addObjectCreate(fullPrefix + "/filter",
"org.apache.catalina.deploy.FilterDef");
digester.addSetNext(fullPrefix + "/filter",
"addFilter",
"org.apache.catalina.deploy.FilterDef");
digester.addCallMethod(fullPrefix + "/filter/description",
"setDescription", 0);
digester.addCallMethod(fullPrefix + "/filter/display-name",
"setDisplayName", 0);
digester.addCallMethod(fullPrefix + "/filter/filter-class",
"setFilterClass", 0);
digester.addCallMethod(fullPrefix + "/filter/filter-name",
"setFilterName", 0);
digester.addCallMethod(fullPrefix + "/filter/icon/large-icon",
"setLargeIcon", 0);
digester.addCallMethod(fullPrefix + "/filter/icon/small-icon",
"setSmallIcon", 0);
digester.addCallMethod(fullPrefix + "/filter/async-supported",
"setAsyncSupported", 0);
digester.addCallMethod(fullPrefix + "/filter/init-param",
"addInitParameter", 2);
digester.addCallParam(fullPrefix + "/filter/init-param/param-name",
0);
digester.addCallParam(fullPrefix + "/filter/init-param/param-value",
1);
digester.addObjectCreate(fullPrefix + "/filter-mapping",
"org.apache.catalina.deploy.FilterMap");
digester.addSetNext(fullPrefix + "/filter-mapping",
"addFilterMapping",
"org.apache.catalina.deploy.FilterMap");
digester.addCallMethod(fullPrefix + "/filter-mapping/filter-name",
"setFilterName", 0);
digester.addCallMethod(fullPrefix + "/filter-mapping/servlet-name",
"addServletName", 0);
digester.addCallMethod(fullPrefix + "/filter-mapping/url-pattern",
"addURLPattern", 0);
digester.addCallMethod(fullPrefix + "/filter-mapping/dispatcher",
"setDispatcher", 0);
digester.addCallMethod(fullPrefix + "/listener/listener-class",
"addListener", 0);
digester.addRule(fullPrefix + "/jsp-config",
jspConfig);
digester.addObjectCreate(fullPrefix + "/jsp-config/jsp-property-group",
"org.apache.catalina.deploy.JspPropertyGroup");
digester.addSetNext(fullPrefix + "/jsp-config/jsp-property-group",
"addJspPropertyGroup",
"org.apache.catalina.deploy.JspPropertyGroup");
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/deferred-syntax-allowed-as-literal",
"setDeferredSyntax", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/el-ignored",
"setElIgnored", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/include-coda",
"addIncludeCoda", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/include-prelude",
"addIncludePrelude", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/is-xml",
"setIsXml", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/page-encoding",
"setPageEncoding", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/scripting-invalid",
"setScriptingInvalid", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/trim-directive-whitespaces",
"setTrimWhitespace", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/url-pattern",
"addUrlPattern", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/default-content-type",
"setDefaultContentType", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/buffer",
"setBuffer", 0);
digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/error-on-undeclared-namespace",
"setErrorOnUndeclaredNamespace", 0);
digester.addRule(fullPrefix + "/login-config",
loginConfig);
digester.addObjectCreate(fullPrefix + "/login-config",
"org.apache.catalina.deploy.LoginConfig");
digester.addSetNext(fullPrefix + "/login-config",
"setLoginConfig",
"org.apache.catalina.deploy.LoginConfig");
digester.addCallMethod(fullPrefix + "/login-config/auth-method",
"setAuthMethod", 0);
digester.addCallMethod(fullPrefix + "/login-config/realm-name",
"setRealmName", 0);
digester.addCallMethod(fullPrefix + "/login-config/form-login-config/form-error-page",
"setErrorPage", 0);
digester.addCallMethod(fullPrefix + "/login-config/form-login-config/form-login-page",
"setLoginPage", 0);
digester.addCallMethod(fullPrefix + "/mime-mapping",
"addMimeMapping", 2);
digester.addCallParam(fullPrefix + "/mime-mapping/extension", 0);
digester.addCallParam(fullPrefix + "/mime-mapping/mime-type", 1);
digester.addObjectCreate(fullPrefix + "/security-constraint",
"org.apache.catalina.deploy.SecurityConstraint");
digester.addSetNext(fullPrefix + "/security-constraint",
"addSecurityConstraint",
"org.apache.catalina.deploy.SecurityConstraint");
digester.addRule(fullPrefix + "/security-constraint/auth-constraint",
new SetAuthConstraintRule());
digester.addCallMethod(fullPrefix + "/security-constraint/auth-constraint/role-name",
"addAuthRole", 0);
digester.addCallMethod(fullPrefix + "/security-constraint/display-name",
"setDisplayName", 0);
digester.addCallMethod(fullPrefix + "/security-constraint/user-data-constraint/transport-guarantee",
"setUserConstraint", 0);
digester.addObjectCreate(fullPrefix + "/security-constraint/web-resource-collection",
"org.apache.catalina.deploy.SecurityCollection");
digester.addSetNext(fullPrefix + "/security-constraint/web-resource-collection",
"addCollection",
"org.apache.catalina.deploy.SecurityCollection");
digester.addCallMethod(fullPrefix + "/security-constraint/web-resource-collection/http-method",
"addMethod", 0);
digester.addCallMethod(fullPrefix + "/security-constraint/web-resource-collection/http-method-omission",
"addOmittedMethod", 0);
digester.addCallMethod(fullPrefix + "/security-constraint/web-resource-collection/url-pattern",
"addPattern", 0);
digester.addCallMethod(fullPrefix + "/security-constraint/web-resource-collection/web-resource-name",
"setName", 0);
digester.addCallMethod(fullPrefix + "/security-role/role-name",
"addSecurityRole", 0);
digester.addRule(fullPrefix + "/servlet",
new ServletDefCreateRule());
digester.addSetNext(fullPrefix + "/servlet",
"addServlet",
"org.apache.catalina.deploy.ServletDef");
digester.addCallMethod(fullPrefix + "/servlet/init-param",
"addInitParameter", 2);
digester.addCallParam(fullPrefix + "/servlet/init-param/param-name",
0);
digester.addCallParam(fullPrefix + "/servlet/init-param/param-value",
1);
digester.addCallMethod(fullPrefix + "/servlet/jsp-file",
"setJspFile", 0);
digester.addCallMethod(fullPrefix + "/servlet/load-on-startup",
"setLoadOnStartup", 0);
digester.addCallMethod(fullPrefix + "/servlet/run-as/role-name",
"setRunAs", 0);
digester.addObjectCreate(fullPrefix + "/servlet/security-role-ref",
"org.apache.catalina.deploy.SecurityRoleRef");
digester.addSetNext(fullPrefix + "/servlet/security-role-ref",
"addSecurityRoleRef",
"org.apache.catalina.deploy.SecurityRoleRef");
digester.addCallMethod(fullPrefix + "/servlet/security-role-ref/role-link",
"setLink", 0);
digester.addCallMethod(fullPrefix + "/servlet/security-role-ref/role-name",
"setName", 0);
digester.addCallMethod(fullPrefix + "/servlet/servlet-class",
"setServletClass", 0);
digester.addCallMethod(fullPrefix + "/servlet/servlet-name",
"setServletName", 0);
digester.addObjectCreate(fullPrefix + "/servlet/multipart-config",
"org.apache.catalina.deploy.MultipartDef");
digester.addSetNext(fullPrefix + "/servlet/multipart-config",
"setMultipartDef",
"org.apache.catalina.deploy.MultipartDef");
digester.addCallMethod(fullPrefix + "/servlet/multipart-config/location",
"setLocation", 0);
digester.addCallMethod(fullPrefix + "/servlet/multipart-config/max-file-size",
"setMaxFileSize", 0);
digester.addCallMethod(fullPrefix + "/servlet/multipart-config/max-request-size",
"setMaxRequestSize", 0);
digester.addCallMethod(fullPrefix + "/servlet/multipart-config/file-size-threshold",
"setFileSizeThreshold", 0);
digester.addCallMethod(fullPrefix + "/servlet/async-supported",
"setAsyncSupported", 0);
digester.addCallMethod(fullPrefix + "/servlet/enabled",
"setEnabled", 0);
digester.addRule(fullPrefix + "/servlet-mapping",
new CallMethodMultiRule("addServletMapping", 2, 0));
digester.addCallParam(fullPrefix + "/servlet-mapping/servlet-name", 1);
digester.addRule(fullPrefix + "/servlet-mapping/url-pattern", new CallParamMultiRule(0));
digester.addRule(fullPrefix + "/session-config", sessionConfig);
digester.addObjectCreate(fullPrefix + "/session-config",
"org.apache.catalina.deploy.SessionConfig");
digester.addSetNext(fullPrefix + "/session-config", "setSessionConfig",
"org.apache.catalina.deploy.SessionConfig");
digester.addCallMethod(fullPrefix + "/session-config/session-timeout",
"setSessionTimeout", 0);
digester.addCallMethod(fullPrefix + "/session-config/cookie-config/name",
"setCookieName", 0);
digester.addCallMethod(fullPrefix + "/session-config/cookie-config/domain",
"setCookieDomain", 0);
digester.addCallMethod(fullPrefix + "/session-config/cookie-config/path",
"setCookiePath", 0);
digester.addCallMethod(fullPrefix + "/session-config/cookie-config/comment",
"setCookieComment", 0);
digester.addCallMethod(fullPrefix + "/session-config/cookie-config/http-only",
"setCookieHttpOnly", 0);
digester.addCallMethod(fullPrefix + "/session-config/cookie-config/secure",
"setCookieSecure", 0);
digester.addCallMethod(fullPrefix + "/session-config/cookie-config/max-age",
"setCookieMaxAge", 0);
digester.addCallMethod(fullPrefix + "/session-config/tracking-mode",
"addSessionTrackingMode", 0);
// Taglibs pre Servlet 2.4
digester.addRule(fullPrefix + "/taglib", new TaglibLocationRule(false));
digester.addCallMethod(fullPrefix + "/taglib",
"addTaglib", 2);
digester.addCallParam(fullPrefix + "/taglib/taglib-location", 1);
digester.addCallParam(fullPrefix + "/taglib/taglib-uri", 0);
// Taglibs Servlet 2.4 onwards
digester.addRule(fullPrefix + "/jsp-config/taglib", new TaglibLocationRule(true));
digester.addCallMethod(fullPrefix + "/jsp-config/taglib",
"addTaglib", 2);
digester.addCallParam(fullPrefix + "/jsp-config/taglib/taglib-location", 1);
digester.addCallParam(fullPrefix + "/jsp-config/taglib/taglib-uri", 0);
digester.addCallMethod(fullPrefix + "/welcome-file-list/welcome-file",
"addWelcomeFile", 0);
digester.addCallMethod(fullPrefix + "/locale-encoding-mapping-list/locale-encoding-mapping",
"addLocaleEncodingMapping", 2);
digester.addCallParam(fullPrefix + "/locale-encoding-mapping-list/locale-encoding-mapping/locale", 0);
digester.addCallParam(fullPrefix + "/locale-encoding-mapping-list/locale-encoding-mapping/encoding", 1);
digester.addRule(fullPrefix + "/post-construct",
new LifecycleCallbackRule("addPostConstructMethods", 2, true));
digester.addCallParam(fullPrefix + "/post-construct/lifecycle-callback-class", 0);
digester.addCallParam(fullPrefix + "/post-construct/lifecycle-callback-method", 1);
digester.addRule(fullPrefix + "/pre-destroy",
new LifecycleCallbackRule("addPreDestroyMethods", 2, false));
digester.addCallParam(fullPrefix + "/pre-destroy/lifecycle-callback-class", 0);
digester.addCallParam(fullPrefix + "/pre-destroy/lifecycle-callback-method", 1);
}
其中的fullPrefix
对应的就是上面两种xml文件的根元素。这里需要引入Digester
中另外两个内置解析规则类CallMethodRule
和CallParamRule
,分别来源于digester.addCallMethod(String pattern, String methodName, int paramCount)
和digester.addCallParam(String pattern, int paramIndex)
,前者如果pattern
匹配成功,会调用当前栈顶元素的名为methodName
,属性数量为paramCount
的方法;后者需要与前者配合使用,其含义为找到与pattern
匹配的标签对应的值,该值作为前者调用方法的第paramIndex
参数的值传入,举个例子来说
相信写过web程序的读者都知道
的含义,当解析到第一句时会调用此时
digester
内部栈栈顶元素的
addContextParam(String param, String value)
方法,该方法有两个参数,当解析到子标签
时,将该标签的值对应方法中的第一个参数
param
,当解析到子标签
时,将该标签的值对应方法中的第二个参数
value
从代码清单2中我们还可以知道
对应的实体为
FilterDef
,
对应的实体为
FilterMap
,
对应的实体又在
ServletDefCreateRule
中给出定义,为
ServletDef
。需要说明的一点是,这里只是定义了
web.xml
的解析规则,Tomcat将这些解析规则封装到了图22中的两个成员变量
webRuleSet
和
webFragmentRuleSet
中,真正的解析是在图20中,当
ContextConfig
监听到事件
Lifecycle.CONFIGURE_START_EVENT
,进而调用
configureStart()
时才发生的,具体的流程将在生命周期的补充文章中讲解
后记
未来计划用两到三篇文章对Tomcat容器生命周期相关知识点进行补充分析,主要包括两部分内容:
- 容器的初始化和启动流程
- 各个容器相关监听器在Tomcat运行时的作用
完成这些前期准备工作后再用两到三篇文章对Tomcat如何接收处理请求,又如何正确的将请求分发到对应的war包,对应的Servlet中的