Tomcat架構(gòu)中各個組件及組件間關系(二)

前言
由于換工作的原因片仿,需要融入新的開發(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ī)則如下

圖1. <Service>標簽的解析規(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>標簽如下所示
圖2. server.xml中Service標簽

其屬性只有name一個,那我們猜想在StandardService中可能存在一個該屬性對應的set方法玖详,看下StandardService的代碼發(fā)現(xiàn)確實如此
圖3. StandardService中setName方法

這里有一個小坑需要說明一下把介,實際上標簽對應的實體類并不一定存在標簽屬性對應的set方法,并且也不是存在對應屬性的set方法就會調(diào)用蟋座,理解這個細節(jié)我們需要進入到SetPropertiesRule類的begin()方法中
圖4. SetPropertiesRule類的begin方法

紅框處存在三個判斷拗踢,第一個digester.isFakeAtrribute(top, name),其中top是當前Digester內(nèi)部棧中棧頂元素向臀,對于<Service>而言棧頂元素就是StandardService巢墅,name是每一個屬性的名稱,isFakeAtrribute具體的邏輯如下
圖5. isFakeAtrribute方法

該方法實際上就是在一個Map<Class,List<String>>的集合中判斷某個類是否存在某個名稱的屬性券膀,如果存在就返回true君纫,進而不去調(diào)用該屬性的set方法,那么有哪些屬性被放在了這個“假屬性”集合中呢芹彬?我們回頭看Catalina中定義server.xml解析規(guī)則的方法
圖6. Catalina的createStartDigester方法

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ù)的類型梧田。在這里因為當前棧頂元素為StandardServiceaddSetNext會調(diào)用StandardServeraddService(Service service)方法侧蘸,將當前StandardService與其父元素StandardServer建立關聯(lián)裁眯,涉及相關代碼如下

圖7. StandardServer中addService方法

我們可以稍微總結(jié)一下Digester內(nèi)置的三大解析規(guī)則類對應的用途

Rule 對應方法 用途
ObjectCreateRule addObjectCreate 根據(jù)匹配解析模式創(chuàng)建對應標簽的實體類
SetPropertiesRule addSetProperties 根據(jù)匹配解析模式為對應標簽實體類設置相關屬性
SetNextRule addSetNext 建立標簽對應實體之間子父類關系

至此<Service>標簽的規(guī)則配置及解析流程分析完畢,我們接著看<Connector>標簽

圖8. Connector標簽的解析規(guī)則

創(chuàng)建<Connector>對象的規(guī)則和<Service>規(guī)則不太一樣讳癌,并沒有使用Digester內(nèi)建的ObjectCreateRule穿稳,而是自己繼承Rule創(chuàng)建了ConnectorCreateRule。前文中分析過晌坤,當解析到對應標簽的開始處會調(diào)用規(guī)則類的begin()逢艘,我們來看看它做了什么
圖9. ObjectCreateRule的執(zhí)行流程

方法中首先取出此時棧頂元素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初始化流程
圖10. Connector構(gòu)造器

Connector構(gòu)造器會繼續(xù)調(diào)用setProtocol(String protocol)方法信粮,并將協(xié)議對應的字符串傳入
圖11. 確認協(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屬性
圖12. SetAllPropertiesRule

begin()方法中不僅按上面提到的流程對屬性進行了篩選,而且根據(jù)該規(guī)則中設置的排除屬性數(shù)組再一次進行了過濾称龙。同樣的留拾,在addSetNext方法中會調(diào)用父標簽StandardServiceaddConnector(Connector)蹦玫,從而建立父子關聯(lián)關系
圖13. StandardService中addConnector方法

在方法中使用了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引擎糯耍,其下可以存在多個HostContext子容器
圖14. Engine相關解析規(guī)則

乍一看貌似<Engine>相關的rule特別多,但仔細一看其實都是套路囊嘉,按照上面的分析方式都能一一拿下温技,這里只說一些重點和不同的部分。規(guī)則中為<Engine>添加了一個名為EngineConfig的Listener扭粱,用于對StandardEngine組件的生命周期監(jiān)控
圖15. EngineConfig的所有邏輯

從圖中可以看到該類在事件為start和stop時會進行日志的打印舵鳞,此外并沒有進行其他的操作,在StandardEngine初始化時存在一個管道Pipeline和閥門Valve的概念
圖16. StandardEngine構(gòu)造器

Tomcat中為了更高效的處理請求琢蛤,內(nèi)部設計了PipelineValve的概念蜓堕,相當于Servlet中的Filter和FilterChain,管道中可以通過addValve(Valve)添加或通過removeValve(Valve)移除多個閥門博其,而有一種閥門被稱為基礎閥門套才,該閥門總是最后一個執(zhí)行的,比如這里的StandardEngineValve慕淡,關于兩者的詳細分析會在后續(xù)文章開展霜旧,這里不做累述。參數(shù)backgroundProcessorDelayContainerBase中的內(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ī)則定義如下
圖17. Host相關解析規(guī)則

<Host>對應的實體類為StandardHost,在初始化時也給Host容器中的管道添加了一個基礎閥門StandardHostValve钠署。同StandardEngine一樣糠聪,Tomcat也為StandardHost添加了一個監(jiān)聽器HostConfig,但其功能遠比EngineConfig復雜很多

圖18. HostConfig中l(wèi)ifecycleEvent

它根據(jù)不同的事件類型對web應用的進行相應的檢查發(fā)布谐鼎,停止以及和上面提到的ContainerBackgroundProcessor線程結(jié)合起來監(jiān)控應用是否需要reload等功能舰蟆,這部分內(nèi)容和容器的生命周期關系更加緊密,且可講的內(nèi)容較多狸棍,將放在生命周期強化的第二部分講解
一個<Context>可以認為對應一個webapps下的目錄身害,或者一個war包。代表虛擬主機的<Host>下可以存在多個<Context>標簽隔缀,<Context>對應的解析規(guī)則也是繼承RuleSetBase創(chuàng)建了自己的規(guī)則集合ContextRuleSet
圖19. ContextRuleSet

標簽對應的實體是StandardContext题造,也存在基礎閥門StandardContextValve,添加了對應的監(jiān)聽器ContextConfig猾瘸,在對該監(jiān)聽器進行說明之前不知道大家想過沒有界赔,到目前為止,我們一直在討論server.xml文件的解析牵触,那其他的xml文件淮悼,比如context.xmlweb.xml是什么時候解析的呢揽思?為了回答這一問題袜腥,我們來看一下ContextConfig是如何處理監(jiān)聽事件發(fā)生的
圖20. ContextConfig處理監(jiān)聽事件邏輯

Tomcat的生命周期中曾給出Tomcat生命周期流轉(zhuǎn)圖和全部生命周期狀態(tài)字段,結(jié)合上圖兩處紅框中的Lifecycle類型可知钉汗,Lifecycle.AFTER_INIT_EVENT發(fā)生在Lifecycle.CONFIGURE_START_EVENT之前羹令,而前者的init()中就定義了解析web.xml文件的所有規(guī)則
圖21. ContextConfig中init方法

繼續(xù)深入
圖22. createWebXmlDigester(boolean namespaceAware, boolean validation)

WebRuleSet同樣繼承了RuleSet鲤屡,web.xml存在兩種形式,一種是我們“通掣3蓿”意義上酒来,放在每一個war包內(nèi)的webapps/WEB-INF/web.xml,該配置文件是以<web-app>作為根元素的肪凛;另一種是為了支持Servlet3.0新特性將web.xml分成多個小部分堰汉,運行時再將各個部分聚集起來解析的配置文件web-fragment.xml,該文件是以<web-fragment>作為根元素伟墙。代碼清單2WebRuleSet設置的所有標簽的解析規(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ī)則類CallMethodRuleCallParamRule,分別來源于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ù)的值傳入邻梆,舉個例子來說

圖23. 舉例說明CallMethodRule和CallParamRule兩種規(guī)則

相信寫過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中的兩個成員變量webRuleSetwebFragmentRuleSet中惯吕,真正的解析是在圖20中,當ContextConfig監(jiān)聽到事件Lifecycle.CONFIGURE_START_EVENT怕午,進而調(diào)用configureStart()時才發(fā)生的废登,具體的流程將在生命周期的補充文章中講解

后記
未來計劃用兩到三篇文章對Tomcat容器生命周期相關知識點進行補充分析,主要包括兩部分內(nèi)容:

  1. 容器的初始化和啟動流程
  2. 各個容器相關監(jiān)聽器在Tomcat運行時的作用
    完成這些前期準備工作后再用兩到三篇文章對Tomcat如何接收處理請求郁惜,又如何正確的將請求分發(fā)到對應的war包堡距,對應的Servlet中的
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子羽戒,更是在濱河造成了極大的恐慌缤沦,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件易稠,死亡現(xiàn)場離奇詭異疚俱,居然都是意外死亡,警方通過查閱死者的電腦和手機缩多,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來养晋,“玉大人衬吆,你說我怎么就攤上這事∩” “怎么了逊抡?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長零酪。 經(jīng)常有香客問我冒嫡,道長,這世上最難降的妖魔是什么四苇? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任孝凌,我火速辦了婚禮,結(jié)果婚禮上月腋,老公的妹妹穿的比我還像新娘蟀架。我一直安慰自己,他們只是感情好榆骚,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布片拍。 她就那樣靜靜地躺著,像睡著了一般妓肢。 火紅的嫁衣襯著肌膚如雪捌省。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天碉钠,我揣著相機與錄音纲缓,去河邊找鬼。 笑死放钦,一個胖子當著我的面吹牛色徘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播操禀,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼褂策,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起斤寂,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤耿焊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后遍搞,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體罗侯,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年溪猿,在試婚紗的時候發(fā)現(xiàn)自己被綠了钩杰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡诊县,死狀恐怖讲弄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情依痊,我是刑警寧澤避除,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站胸嘁,受9級特大地震影響瓶摆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜性宏,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一群井、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧衔沼,春花似錦蝌借、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至凝化,卻和暖如春稍坯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背搓劫。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工瞧哟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人枪向。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓勤揩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親秘蛔。 傳聞我的和親對象是個殘疾皇子陨亡,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理傍衡,服務發(fā)現(xiàn),斷路器负蠕,智...
    卡卡羅2017閱讀 134,652評論 18 139
  • 前言借著上次對Tomcat類加載機制的分析蛙埂,就想著看都看了,何不再看看Tomcat內(nèi)部的實現(xiàn)原理和架構(gòu)設計遮糖,向優(yōu)秀...
    寶之家閱讀 3,280評論 1 5
  • 1. Java基礎部分 基礎部分的順序:基本語法绣的,類相關的語法,內(nèi)部類的語法欲账,繼承相關的語法屡江,異常的語法,線程的語...
    子非魚_t_閱讀 31,623評論 18 399
  • 從三月份找實習到現(xiàn)在赛不,面了一些公司盼理,掛了不少,但最終還是拿到小米俄删、百度、阿里奏路、京東畴椰、新浪、CVTE鸽粉、樂視家的研發(fā)崗...
    時芥藍閱讀 42,240評論 11 349
  • +今天是今日有所思第94天斜脂。 昨天清晨重讀了《財富自由之路》24日的概念后,鬼使神差地選了個題目《如何選書》触机,不料...
    荒原蒼狼閱讀 231評論 0 0