美文网首页
Tomcat架构中各个组件及组件间关系(二)

Tomcat架构中各个组件及组件间关系(二)

作者: 宝之家 | 来源:发表于2017-11-29 23:09 被阅读0次

    前言
    由于换工作的原因,需要融入新的开发团队,开展新的业务征途,因此,距离上一次更新博客已有一段时间,现在稍微稳定下来可以继续Tomcat源码的分析。在回顾思路时发现,之前对于Tomcat组件和生命周期的文章主要从宏观角度分析了两者执行的大体流程,对于细节点的分析有所欠缺,而这些细节可能又是后期理解 “Tomcat处理请求响应” 这种重量级流程的前提。为了更好的温故而知新,未来将对Tomcat架构中各个组件及组件间关系Tomcat的生命周期两篇文章进行更深程度的剖析,本文是其中的第一部分,主要强化的内容如下:

    • Digester解析xml文件模式的详细分析,结合之前的文章,读者会了解到Tomcat中涉及到的所有关键解析规则和原理
    • 其他Connector、Container相关组件解析的详细过程。在之前的文章中,仅以<Server>顶层标签举例,并不涉及<Engine><Host><Context>等“子标签”,而Tomcat标签的解析越往“子标签” 越复杂,越接近Tomcat处理请求响应的核心,因此为了进一步的深入也需要将“子标签”的解析吃透

    Tomcat架构中各个组件及组件间关系中,我们已对Digester类工作的大体思路进行了分析,并且以<Server>标签的解析进行了举例,<Service><Server>的子标签,对应的Digester解析规则如下

    图1. <Service>标签的解析规则
    addObjectCreate(String pattern, String className, String attributeName)底层使用的规则为ObjectCreateRule,方法的第一个参数是pattern,表明了解析到什么标签才会使用配置的规则对标签的内容进行解析,和正则表达式匹配的作用类似。比如上图中的pattern为Server/Service表示解析到<Server>下的<Service>标签时运用规则进行解析,这里用/表示一种父子关系。 第二个参数className很明显表示标签对应的java实体类,从上图中来说<Service>标签对应的实体实际上就是StandardService,其实该参数是一个可选参数,可以传null,用第三个参数attributeName在运行时指定该标签对应的类,以图中举例就是说<Service>标签可以存在一个属性,属性名为className,当第二个参数没有指定时,Digester会自动解析该属性,并通过反射生成该类的实例再压入Digester内部的栈顶。
    addSetProperties(String pattern)底层使用的规则为SetPropertiesRule,方法唯一的参数也是pattern,同样表示遇到何种标签才进行解析,SetPropertiesRule规则用于解析标签对应的属性。以上图举例,<Service>标签如下所示
    图2. server.xml中Service标签
    其属性只有name一个,那我们猜想在StandardService中可能存在一个该属性对应的set方法,看下StandardService的代码发现确实如此
    图3. StandardService中setName方法
    这里有一个小坑需要说明一下,实际上标签对应的实体类并不一定存在标签属性对应的set方法,并且也不是存在对应属性的set方法就会调用,理解这个细节我们需要进入到SetPropertiesRule类的begin()方法中
    图4. SetPropertiesRule类的begin方法
    红框处存在三个判断,第一个digester.isFakeAtrribute(top, name),其中top是当前Digester内部栈中栈顶元素,对于<Service>而言栈顶元素就是StandardServicename是每一个属性的名称,isFakeAtrribute具体的逻辑如下
    图5. isFakeAtrribute方法
    该方法实际上就是在一个Map<Class,List<String>>的集合中判断某个类是否存在某个名称的属性,如果存在就返回true,进而不去调用该属性的set方法,那么有哪些属性被放在了这个“假属性”集合中呢?我们回头看Catalina中定义server.xml解析规则的方法
    图6. Catalina的createStartDigester方法
    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,方法的第一个参数指明了触发该规则的具体模式,第二个参数表明调用父标签对应实体的方法名称,第三个参数就是方法参数的类型。在这里因为当前栈顶元素为StandardServiceaddSetNext会调用StandardServeraddService(Service service)方法,将当前StandardService与其父元素StandardServer建立关联,涉及相关代码如下

    图7. StandardServer中addService方法
    我们可以稍微总结一下Digester内置的三大解析规则类对应的用途
    Rule 对应方法 用途
    ObjectCreateRule addObjectCreate 根据匹配解析模式创建对应标签的实体类
    SetPropertiesRule addSetProperties 根据匹配解析模式为对应标签实体类设置相关属性
    SetNextRule addSetNext 建立标签对应实体之间子父类关系

    至此<Service>标签的规则配置及解析流程分析完毕,我们接着看<Connector>标签

    图8. Connector标签的解析规则
    创建<Connector>对象的规则和<Service>规则不太一样,并没有使用Digester內建的ObjectCreateRule,而是自己继承Rule创建了ConnectorCreateRule。前文中分析过,当解析到对应标签的开始处会调用规则类的begin(),我们来看看它做了什么
    图9. ObjectCreateRule的执行流程
    方法中首先取出此时栈顶元素StandardService(Connector尚未创建),再从包含所有<Connector>标签属性的attributes中查找是否存在exector属性,存在最终会调用_setExecutor(Connector, Executor)方法,该方法的主要作用是设置处理端到端连接的线程池,默认情况下server.xml中并不会事先设置该线程池,但即便不设置,之后在Tomcat启动时也会默认创建一个,在后面分析启动流程时会详细分析,这里暂先按默认未设置线程池流程走。之后会根据protocol属性创建Connector对象,基于Tomcat架构中各个组件及组件间关系中给出的server.xml可知,<Connector>标签共有两种协议,一种是HTTP/1.1,另一种是AJP/1.3。前者大家很清楚是HTTP协议的1.1版本,后者一般用于web容器之间通信,比HTTP协议在web容器间拥有更高的吞吐量。因为存在两种协议,那就会存在两个Connector实体,为了突出重点,我们只分析最常用的HTTP协议对应的Connector初始化流程
    图10. Connector构造器
    Connector构造器会继续调用setProtocol(String protocol)方法,并将协议对应的字符串传入
    图11. 确认协议对应处理器类型
    第一个判断涉及到一种apr请求处理方法,可以将其理解为一种高效的IO模式,底层基于JNI技术调用操作系统级别的IO接口,在默认情况下也是关闭的。因此上述代码最终会调用setProtocolHandlerClassName("org.apache.coyote.http11.Http11Protocol"),将Connector中的成员变量ProtocolHandler置为Http11Protocol,该类在处理请求响应的流程中起到了重要作用,后续文章会详细分析,这里记住即可
    <Connector>在处理属性是也添加了名为SetAllPropertiesRule的规则,该规则接收了一个排除属性的数组,其中仅包含executor属性
    图12. SetAllPropertiesRule
    begin()方法中不仅按上面提到的流程对属性进行了筛选,而且根据该规则中设置的排除属性数组再一次进行了过滤。同样的,在addSetNext方法中会调用父标签StandardServiceaddConnector(Connector),从而建立父子关联关系
    图13. StandardService中addConnector方法
    在方法中使用了synchronized代码块解决了并发访问下新增Connector被覆盖的问题,在Tomcat的生命周期中说到,每一个容器都一个生命周期状态的概念,这里getState()就获得了此时Connector的状态,在刚创建时容器的state为NEWavailable属性值为false,并不会立即启动Connector容器,至此<Connector>的解析过程也分析完毕
    Tomcat从整体架构上可以分为两大部分:监听请求并生成对应Request和Response的Connector连接器,以及处理请求和控制tomcat容器运转的Container<Engine>标签就是Container的顶层组件,每一个Engine相当于一个Servlet引擎,其下可以存在多个HostContext子容器
    图14. Engine相关解析规则
    乍一看貌似<Engine>相关的rule特别多,但仔细一看其实都是套路,按照上面的分析方式都能一一拿下,这里只说一些重点和不同的部分。规则中为<Engine>添加了一个名为EngineConfig的Listener,用于对StandardEngine组件的生命周期监控
    图15. EngineConfig的所有逻辑
    从图中可以看到该类在事件为start和stop时会进行日志的打印,此外并没有进行其他的操作,在StandardEngine初始化时存在一个管道Pipeline和阀门Valve的概念
    图16. StandardEngine构造器
    Tomcat中为了更高效的处理请求,内部设计了PipelineValve的概念,相当于Servlet中的Filter和FilterChain,管道中可以通过addValve(Valve)添加或通过removeValve(Valve)移除多个阀门,而有一种阀门被称为基础阀门,该阀门总是最后一个执行的,比如这里的StandardEngineValve,关于两者的详细分析会在后续文章开展,这里不做累述。参数backgroundProcessorDelayContainerBase中的内部类ContainerBackgroundProcessor有关,该类实现了Runnable接口,用于检测war包中的类文件是否改动,是否需要重新加载,而参数乘以默认的基数就是执行的间隔时间,具体的处理流程后续文章同样会讲到。setJvmRoute()是给该机器设置一个唯一的标识,当有多台机器组成cluster时,每台机器都会用这唯一的标识代表自身在集群中的位置
    回到EngineRuleSet的规则定义上,我们发现Tomcat还为每一个StandardEngine添加了RealmRuleSet,该规则对应<Engine>的子标签<Realm>,此标签引出了一个“域”的概念,我们可以将多个web应用划分成多个域,给每个域设定不同的访问权限,只有拥有对应域访问权限的角色才能访问对应的web应用,因此该规则的设定主要为了安全访问和权限管理
    一个<Host>表示一个虚拟主机,其下可以存在多个<Context>标签,<Host>标签对应的规则定义如下
    图17. Host相关解析规则

    <Host>对应的实体类为StandardHost,在初始化时也给Host容器中的管道添加了一个基础阀门StandardHostValve。同StandardEngine一样,Tomcat也为StandardHost添加了一个监听器HostConfig,但其功能远比EngineConfig复杂很多

    图18. HostConfig中lifecycleEvent
    它根据不同的事件类型对web应用的进行相应的检查发布,停止以及和上面提到的ContainerBackgroundProcessor线程结合起来监控应用是否需要reload等功能,这部分内容和容器的生命周期关系更加紧密,且可讲的内容较多,将放在生命周期强化的第二部分讲解
    一个<Context>可以认为对应一个webapps下的目录,或者一个war包。代表虚拟主机的<Host>下可以存在多个<Context>标签,<Context>对应的解析规则也是继承RuleSetBase创建了自己的规则集合ContextRuleSet
    图19. ContextRuleSet
    标签对应的实体是StandardContext,也存在基础阀门StandardContextValve,添加了对应的监听器ContextConfig,在对该监听器进行说明之前不知道大家想过没有,到目前为止,我们一直在讨论server.xml文件的解析,那其他的xml文件,比如context.xmlweb.xml是什么时候解析的呢?为了回答这一问题,我们来看一下ContextConfig是如何处理监听事件发生的
    图20. ContextConfig处理监听事件逻辑
    Tomcat的生命周期中曾给出Tomcat生命周期流转图和全部生命周期状态字段,结合上图两处红框中的Lifecycle类型可知,Lifecycle.AFTER_INIT_EVENT发生在Lifecycle.CONFIGURE_START_EVENT之前,而前者的init()中就定义了解析web.xml文件的所有规则
    图21. ContextConfig中init方法
    继续深入
    图22. createWebXmlDigester(boolean namespaceAware, boolean validation)
    WebRuleSet同样继承了RuleSetweb.xml存在两种形式,一种是我们“通常”意义上,放在每一个war包内的webapps/WEB-INF/web.xml,该配置文件是以<web-app>作为根元素的;另一种是为了支持Servlet3.0新特性将web.xml分成多个小部分,运行时再将各个部分聚集起来解析的配置文件web-fragment.xml,该文件是以<web-fragment>作为根元素。代码清单2WebRuleSet设置的所有标签的解析规则
    @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中另外两个内置解析规则类CallMethodRuleCallParamRule,分别来源于digester.addCallMethod(String pattern, String methodName, int paramCount)digester.addCallParam(String pattern, int paramIndex),前者如果pattern匹配成功,会调用当前栈顶元素的名为methodName,属性数量为paramCount的方法;后者需要与前者配合使用,其含义为找到与pattern匹配的标签对应的值,该值作为前者调用方法的第paramIndex参数的值传入,举个例子来说

    图23. 举例说明CallMethodRule和CallParamRule两种规则
    相信写过web程序的读者都知道<context-param>的含义,当解析到第一句时会调用此时digester内部栈栈顶元素的addContextParam(String param, String value)方法,该方法有两个参数,当解析到子标签<param-name>时,将该标签的值对应方法中的第一个参数param,当解析到子标签<param-value>时,将该标签的值对应方法中的第二个参数value
    从代码清单2中我们还可以知道<filter>对应的实体为FilterDef<filter-mapping>对应的实体为FilterMap<servlet>对应的实体又在ServletDefCreateRule中给出定义,为ServletDef。需要说明的一点是,这里只是定义了web.xml的解析规则,Tomcat将这些解析规则封装到了图22中的两个成员变量webRuleSetwebFragmentRuleSet中,真正的解析是在图20中,当ContextConfig监听到事件Lifecycle.CONFIGURE_START_EVENT,进而调用configureStart()时才发生的,具体的流程将在生命周期的补充文章中讲解

    后记
    未来计划用两到三篇文章对Tomcat容器生命周期相关知识点进行补充分析,主要包括两部分内容:

    1. 容器的初始化和启动流程
    2. 各个容器相关监听器在Tomcat运行时的作用
      完成这些前期准备工作后再用两到三篇文章对Tomcat如何接收处理请求,又如何正确的将请求分发到对应的war包,对应的Servlet中的

    相关文章

      网友评论

          本文标题:Tomcat架构中各个组件及组件间关系(二)

          本文链接:https://www.haomeiwen.com/subject/zpurvxtx.html