前言
由于換工作的原因片仿,需要融入新的開發(fā)團隊纹安,開展新的業(yè)務征途,因此砂豌,距離上一次更新博客已有一段時間厢岂,現(xiàn)在稍微穩(wěn)定下來可以繼續(xù)Tomcat源碼的分析。在回顧思路時發(fā)現(xiàn)阳距,之前對于Tomcat組件和生命周期的文章主要從宏觀角度分析了兩者執(zhí)行的大體流程塔粒,對于細節(jié)點的分析有所欠缺,而這些細節(jié)可能又是后期理解 “Tomcat處理請求響應” 這種重量級流程的前提娄涩。為了更好的溫故而知新窗怒,未來將對Tomcat架構(gòu)中各個組件及組件間關系和Tomcat的生命周期兩篇文章進行更深程度的剖析,本文是其中的第一部分蓄拣,主要強化的內(nèi)容如下:
- Digester解析xml文件模式的詳細分析扬虚,結(jié)合之前的文章,讀者會了解到Tomcat中涉及到的所有關鍵解析規(guī)則和原理
- 其他Connector球恤、Container相關組件解析的詳細過程辜昵。在之前的文章中,僅以
<Server>
頂層標簽舉例咽斧,并不涉及<Engine>
堪置、<Host>
、<Context>
等“子標簽”张惹,而Tomcat標簽的解析越往“子標簽” 越復雜舀锨,越接近Tomcat處理請求響應的核心,因此為了進一步的深入也需要將“子標簽”的解析吃透
在Tomcat架構(gòu)中各個組件及組件間關系中宛逗,我們已對Digester類工作的大體思路進行了分析坎匿,并且以<Server>
標簽的解析進行了舉例,<Service>
是<Server>
的子標簽雷激,對應的Digester解析規(guī)則如下
addObjectCreate(String pattern, String className, String attributeName)
底層使用的規(guī)則為ObjectCreateRule
替蔬,方法的第一個參數(shù)是pattern,表明了解析到什么標簽才會使用配置的規(guī)則對標簽的內(nèi)容進行解析屎暇,和正則表達式匹配的作用類似承桥。比如上圖中的pattern為Server/Service
表示解析到<Server>
下的<Service>
標簽時運用規(guī)則進行解析,這里用/
表示一種父子關系根悼。 第二個參數(shù)className
很明顯表示標簽對應的java實體類凶异,從上圖中來說<Service>
標簽對應的實體實際上就是StandardService
蜀撑,其實該參數(shù)是一個可選參數(shù),可以傳null唠帝,用第三個參數(shù)attributeName
在運行時指定該標簽對應的類屯掖,以圖中舉例就是說<Service>
標簽可以存在一個屬性,屬性名為className
襟衰,當?shù)诙€參數(shù)沒有指定時贴铜,Digester
會自動解析該屬性,并通過反射生成該類的實例再壓入Digester
內(nèi)部的棧頂瀑晒。addSetProperties(String pattern)
底層使用的規(guī)則為SetPropertiesRule
绍坝,方法唯一的參數(shù)也是pattern,同樣表示遇到何種標簽才進行解析苔悦,SetPropertiesRule
規(guī)則用于解析標簽對應的屬性轩褐。以上圖舉例,<Service>
標簽如下所示其屬性只有name一個,那我們猜想在
StandardService
中可能存在一個該屬性對應的set方法玖详,看下StandardService
的代碼發(fā)現(xiàn)確實如此這里有一個小坑需要說明一下把介,實際上標簽對應的實體類并不一定存在標簽屬性對應的set方法,并且也不是存在對應屬性的set方法就會調(diào)用蟋座,理解這個細節(jié)我們需要進入到
SetPropertiesRule
類的begin()
方法中紅框處存在三個判斷拗踢,第一個
digester.isFakeAtrribute(top, name)
,其中top
是當前Digester
內(nèi)部棧中棧頂元素向臀,對于<Service>
而言棧頂元素就是StandardService
巢墅,name
是每一個屬性的名稱,isFakeAtrribute
具體的邏輯如下該方法實際上就是在一個
Map<Class,List<String>>
的集合中判斷某個類是否存在某個名稱的屬性券膀,如果存在就返回true君纫,進而不去調(diào)用該屬性的set方法,那么有哪些屬性被放在了這個“假屬性”集合中呢芹彬?我們回頭看Catalina
中定義server.xml
解析規(guī)則的方法Tomcat在創(chuàng)建
Digester
類之前默認添加了key為Object.class
的entry蓄髓,其value為包含className
的集合,結(jié)合圖5代碼的邏輯可知舒帮,如果標簽沒有設置特定的fake attributes双吆,那么總會返回默認的,包含名稱為className
的集合会前。而正常情況下所有的標簽都是沒有設置特定的fake attributes的,也就是說Digester
在解析所有標簽時都會排除名稱為className
的屬性我們再來看圖4判斷中的第二個部分
IntrospectionUtils.setProperty(top, name, value)
匾竿,最終調(diào)用的邏輯如 代碼清單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變量。之后會根據(jù)類的實例得到對象所有的方法岭妖,并與setter進行匹配临庇,匹配成功則直接invoke調(diào)用并返回true反璃,沒有找到對應屬性的set方法則返回false。正是這種處理解釋了上面我們提及的:有些標簽即便配置了相關的屬性也不會調(diào)用對應類的set方法
接下來我們再看第三個解析規(guī)則addSetNext(String pattern, String methodName, String paramType)
假夺,底層使用的規(guī)則為SetNextRule
淮蜈,方法的第一個參數(shù)指明了觸發(fā)該規(guī)則的具體模式,第二個參數(shù)表明調(diào)用父標簽對應實體的方法名稱已卷,第三個參數(shù)就是方法參數(shù)的類型梧田。在這里因為當前棧頂元素為StandardService
,addSetNext
會調(diào)用StandardServer
的addService(Service service)
方法侧蘸,將當前StandardService
與其父元素StandardServer
建立關聯(lián)裁眯,涉及相關代碼如下
我們可以稍微總結(jié)一下
Digester
內(nèi)置的三大解析規(guī)則類對應的用途
Rule | 對應方法 | 用途 |
---|---|---|
ObjectCreateRule |
addObjectCreate |
根據(jù)匹配解析模式創(chuàng)建對應標簽的實體類 |
SetPropertiesRule |
addSetProperties |
根據(jù)匹配解析模式為對應標簽實體類設置相關屬性 |
SetNextRule |
addSetNext |
建立標簽對應實體之間子父類關系 |
至此<Service>
標簽的規(guī)則配置及解析流程分析完畢,我們接著看<Connector>
標簽
創(chuàng)建
<Connector>
對象的規(guī)則和<Service>
規(guī)則不太一樣讳癌,并沒有使用Digester
內(nèi)建的ObjectCreateRule
穿稳,而是自己繼承Rule
創(chuàng)建了ConnectorCreateRule
。前文中分析過晌坤,當解析到對應標簽的開始處會調(diào)用規(guī)則類的begin()
逢艘,我們來看看它做了什么方法中首先取出此時棧頂元素
StandardService
(Connector
尚未創(chuàng)建),再從包含所有<Connector>
標簽屬性的attributes
中查找是否存在exector屬性骤菠,存在最終會調(diào)用_setExecutor(Connector, Executor)
方法它改,該方法的主要作用是設置處理端到端連接的線程池,默認情況下server.xml
中并不會事先設置該線程池娩怎,但即便不設置搔课,之后在Tomcat啟動時也會默認創(chuàng)建一個,在后面分析啟動流程時會詳細分析截亦,這里暫先按默認未設置線程池流程走爬泥。之后會根據(jù)protocol
屬性創(chuàng)建Connector
對象,基于Tomcat架構(gòu)中各個組件及組件間關系中給出的server.xml
可知崩瓤,<Connector>
標簽共有兩種協(xié)議袍啡,一種是HTTP/1.1,另一種是AJP/1.3却桶。前者大家很清楚是HTTP協(xié)議的1.1版本境输,后者一般用于web容器之間通信,比HTTP協(xié)議在web容器間擁有更高的吞吐量颖系。因為存在兩種協(xié)議嗅剖,那就會存在兩個Connector
實體,為了突出重點嘁扼,我們只分析最常用的HTTP協(xié)議對應的Connector
初始化流程Connector
構(gòu)造器會繼續(xù)調(diào)用setProtocol(String protocol)
方法信粮,并將協(xié)議對應的字符串傳入第一個判斷涉及到一種apr請求處理方法,可以將其理解為一種高效的IO模式趁啸,底層基于JNI技術(shù)調(diào)用操作系統(tǒng)級別的IO接口强缘,在默認情況下也是關閉的督惰。因此上述代碼最終會調(diào)用
setProtocolHandlerClassName("org.apache.coyote.http11.Http11Protocol")
,將Connector
中的成員變量ProtocolHandler
置為Http11Protocol
旅掂,該類在處理請求響應的流程中起到了重要作用赏胚,后續(xù)文章會詳細分析,這里記住即可<Connector>
在處理屬性是也添加了名為SetAllPropertiesRule
的規(guī)則商虐,該規(guī)則接收了一個排除屬性的數(shù)組觉阅,其中僅包含executor
屬性begin()
方法中不僅按上面提到的流程對屬性進行了篩選,而且根據(jù)該規(guī)則中設置的排除屬性數(shù)組再一次進行了過濾称龙。同樣的留拾,在addSetNext
方法中會調(diào)用父標簽StandardService
的addConnector(Connector)
蹦玫,從而建立父子關聯(lián)關系在方法中使用了
synchronized
代碼塊解決了并發(fā)訪問下新增Connector
被覆蓋的問題骑脱,在Tomcat的生命周期中說到,每一個容器都一個生命周期狀態(tài)的概念咐吼,這里getState()
就獲得了此時Connector
的狀態(tài)疫向,在剛創(chuàng)建時容器的state為NEW
咳蔚,available
屬性值為false,并不會立即啟動Connector
容器搔驼,至此<Connector>
的解析過程也分析完畢Tomcat從整體架構(gòu)上可以分為兩大部分:監(jiān)聽請求并生成對應Request和Response的
Connector
連接器谈火,以及處理請求和控制tomcat容器運轉(zhuǎn)的Container
。<Engine>
標簽就是Container
的頂層組件舌涨,每一個Engine
相當于一個Servlet引擎糯耍,其下可以存在多個Host
和Context
子容器乍一看貌似
<Engine>
相關的rule特別多,但仔細一看其實都是套路囊嘉,按照上面的分析方式都能一一拿下温技,這里只說一些重點和不同的部分。規(guī)則中為<Engine>
添加了一個名為EngineConfig
的Listener扭粱,用于對StandardEngine
組件的生命周期監(jiān)控從圖中可以看到該類在事件為start和stop時會進行日志的打印舵鳞,此外并沒有進行其他的操作,在
StandardEngine
初始化時存在一個管道Pipeline
和閥門Valve
的概念Tomcat中為了更高效的處理請求琢蛤,內(nèi)部設計了
Pipeline
和Valve
的概念蜓堕,相當于Servlet中的Filter和FilterChain,管道中可以通過addValve(Valve)
添加或通過removeValve(Valve)
移除多個閥門博其,而有一種閥門被稱為基礎閥門套才,該閥門總是最后一個執(zhí)行的,比如這里的StandardEngineValve
慕淡,關于兩者的詳細分析會在后續(xù)文章開展霜旧,這里不做累述。參數(shù)backgroundProcessorDelay
和ContainerBase
中的內(nèi)部類ContainerBackgroundProcessor
有關,該類實現(xiàn)了Runnable
接口挂据,用于檢測war包中的類文件是否改動,是否需要重新加載儿普,而參數(shù)乘以默認的基數(shù)就是執(zhí)行的間隔時間崎逃,具體的處理流程后續(xù)文章同樣會講到。setJvmRoute()
是給該機器設置一個唯一的標識眉孩,當有多臺機器組成cluster時个绍,每臺機器都會用這唯一的標識代表自身在集群中的位置回到
EngineRuleSet
的規(guī)則定義上,我們發(fā)現(xiàn)Tomcat還為每一個StandardEngine
添加了RealmRuleSet
浪汪,該規(guī)則對應<Engine>
的子標簽<Realm>
巴柿,此標簽引出了一個“域”的概念,我們可以將多個web應用劃分成多個域死遭,給每個域設定不同的訪問權(quán)限广恢,只有擁有對應域訪問權(quán)限的角色才能訪問對應的web應用,因此該規(guī)則的設定主要為了安全訪問和權(quán)限管理一個
<Host>
表示一個虛擬主機呀潭,其下可以存在多個<Context>
標簽钉迷,<Host>
標簽對應的規(guī)則定義如下<Host>
對應的實體類為StandardHost
,在初始化時也給Host
容器中的管道添加了一個基礎閥門StandardHostValve
钠署。同StandardEngine
一樣糠聪,Tomcat也為StandardHost
添加了一個監(jiān)聽器HostConfig
,但其功能遠比EngineConfig
復雜很多
它根據(jù)不同的事件類型對web應用的進行相應的檢查發(fā)布谐鼎,停止以及和上面提到的
ContainerBackgroundProcessor
線程結(jié)合起來監(jiān)控應用是否需要reload等功能舰蟆,這部分內(nèi)容和容器的生命周期關系更加緊密,且可講的內(nèi)容較多狸棍,將放在生命周期強化的第二部分講解一個
<Context>
可以認為對應一個webapps下的目錄身害,或者一個war包。代表虛擬主機的<Host>
下可以存在多個<Context>
標簽隔缀,<Context>
對應的解析規(guī)則也是繼承RuleSetBase
創(chuàng)建了自己的規(guī)則集合ContextRuleSet
標簽對應的實體是
StandardContext
题造,也存在基礎閥門StandardContextValve
,添加了對應的監(jiān)聽器ContextConfig
猾瘸,在對該監(jiān)聽器進行說明之前不知道大家想過沒有界赔,到目前為止,我們一直在討論server.xml
文件的解析牵触,那其他的xml文件淮悼,比如context.xml
、web.xml
是什么時候解析的呢揽思?為了回答這一問題袜腥,我們來看一下ContextConfig
是如何處理監(jiān)聽事件發(fā)生的在Tomcat的生命周期中曾給出Tomcat生命周期流轉(zhuǎn)圖和全部生命周期狀態(tài)字段,結(jié)合上圖兩處紅框中的Lifecycle類型可知钉汗,
Lifecycle.AFTER_INIT_EVENT
發(fā)生在Lifecycle.CONFIGURE_START_EVENT
之前羹令,而前者的init()
中就定義了解析web.xml
文件的所有規(guī)則繼續(xù)深入
WebRuleSet
同樣繼承了RuleSet
鲤屡,web.xml
存在兩種形式,一種是我們“通掣3蓿”意義上酒来,放在每一個war包內(nèi)的webapps/WEB-INF/web.xml
,該配置文件是以<web-app>
作為根元素的肪凛;另一種是為了支持Servlet3.0新特性將web.xml
分成多個小部分堰汉,運行時再將各個部分聚集起來解析的配置文件web-fragment.xml
,該文件是以<web-fragment>
作為根元素伟墙。代碼清單2為WebRuleSet
設置的所有標簽的解析規(guī)則
@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
中另外兩個內(nèi)置解析規(guī)則類CallMethodRule
和CallParamRule
,分別來源于digester.addCallMethod(String pattern, String methodName, int paramCount)
和digester.addCallParam(String pattern, int paramIndex)
戳葵,前者如果pattern
匹配成功就乓,會調(diào)用當前棧頂元素的名為methodName
,屬性數(shù)量為paramCount
的方法譬淳;后者需要與前者配合使用档址,其含義為找到與pattern
匹配的標簽對應的值,該值作為前者調(diào)用方法的第paramIndex
參數(shù)的值傳入邻梆,舉個例子來說
相信寫過web程序的讀者都知道
<context-param>
的含義守伸,當解析到第一句時會調(diào)用此時digester
內(nèi)部棧棧頂元素的addContextParam(String param, String value)
方法,該方法有兩個參數(shù)浦妄,當解析到子標簽<param-name>
時尼摹,將該標簽的值對應方法中的第一個參數(shù)param
,當解析到子標簽<param-value>
時剂娄,將該標簽的值對應方法中的第二個參數(shù)value
從代碼清單2中我們還可以知道
<filter>
對應的實體為FilterDef
蠢涝,<filter-mapping>
對應的實體為FilterMap
,<servlet>
對應的實體又在ServletDefCreateRule
中給出定義阅懦,為ServletDef
和二。需要說明的一點是,這里只是定義了web.xml
的解析規(guī)則耳胎,Tomcat將這些解析規(guī)則封裝到了圖22中的兩個成員變量webRuleSet
和webFragmentRuleSet
中惯吕,真正的解析是在圖20中,當ContextConfig
監(jiān)聽到事件Lifecycle.CONFIGURE_START_EVENT
怕午,進而調(diào)用configureStart()
時才發(fā)生的废登,具體的流程將在生命周期的補充文章中講解
后記
未來計劃用兩到三篇文章對Tomcat容器生命周期相關知識點進行補充分析,主要包括兩部分內(nèi)容:
- 容器的初始化和啟動流程
- 各個容器相關監(jiān)聽器在Tomcat運行時的作用
完成這些前期準備工作后再用兩到三篇文章對Tomcat如何接收處理請求郁惜,又如何正確的將請求分發(fā)到對應的war包堡距,對應的Servlet中的