深入刨析tomcat 之---第11篇 how tomcat works( 第15章 ) 如何解析web.xml 文件

writedby 张艳涛 

记得当年是学习jsp的时候,写过web.xml中的标签.在之后的springmvc中也是有关于配置mvc 过滤器 和dispatchServlet的标签,之前是看不懂呢!看到这本how tomcat works之后,现在比较清楚了,

那么就写下自己的理解

在webapps/app1/目录下的web.xml文件

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
  <servlet>
    <servlet-name>Modern</servlet-name>
    <servlet-class>ModernServlet</servlet-class>
  </servlet>
  <servlet>
    <servlet-name>Primitive</servlet-name>
    <servlet-class>PrimitiveServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Modern</servlet-name>
    <url-pattern>/Modern</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>Primitive</servlet-name>
    <url-pattern>/Primitive</url-pattern>
  </servlet-mapping>
</web-app>

这可是标准的web.xml文件,他的作用,用代码表示出来为

 Wrapper wrapper1 = new StandardWrapper();
    wrapper1.setName("Primitive");
    wrapper1.setServletClass("PrimitiveServlet");
    Wrapper wrapper2 = new StandardWrapper();
    wrapper2.setName("Modern");
    wrapper2.setServletClass("ModernServlet");

    Context context = new StandardContext();
    // StandardContext's start method adds a default mapper
    context.setPath("/app1");
    context.setDocBase("app1");

    context.addChild(wrapper1);
    context.addChild(wrapper2);

    LifecycleListener listener = new SimpleContextConfig();
    ((Lifecycle) context).addLifecycleListener(listener);

    Host host = new StandardHost();
    host.addChild(context);
    host.setName("localhost");
    host.setAppBase("webapps");

    Loader loader = new WebappLoader();
    context.setLoader(loader);
    // context.addServletMapping(pattern, name);
    context.addServletMapping("/Primitive", "Primitive");
    context.addServletMapping("/Modern", "Modern");

描述一下,上述行为,上述为tomcat实现的应编码,StandardContext 是一个可以代表一个web应用,这里是我们的app1应用, 对应的标签就是<web-app> ,

StandardWrapper是代表一个Servlet程序,这里就是是我们wrapper1,对应的标签就是<servlet>其中的行为

    wrapper1.setName("Primitive");
    wrapper1.setServletClass("PrimitiveServlet");
对应了

  <servlet-name>Primitive</servlet-name>
  <servlet-class>PrimitiveServlet</servlet-class>

接着就是配置映射关系

  context.addServletMapping("/Primitive", "Primitive");
  context.addServletMapping("/Modern", "Modern");

对应的标签为

  <servlet-mapping>
  <servlet-name>Modern</servlet-name>
  <url-pattern>/Modern</url-pattern>
  </servlet-mapping>

你说的看似是这么回事,那么到底tomcat是如何解析的呢?那么我们看源码说话

HostConfig,站点的配置文件看起

深入刨析tomcat 之---第11篇 how tomcat works( 第15章 ) 如何解析web.xml 文件

先解释一下这个host对象,这个是tomcat的4大容器之一,engine,host,context,wrapper; host代表的是一个站点,比如host的重要的属性有

appBase="webapps"

name="localhost"

children=context的HashMap

那么能看的到,deloyApps代码中是分析webapps目录下的文件,对于每个文件都要新建一个StandardContext对象

深入刨析tomcat 之---第11篇 how tomcat works( 第15章 ) 如何解析web.xml 文件

代码中能看到的设置

context.setPath(contextPath);
context.setDocBase(docBase);

和自己手写BootStrap文件行为是一致(bootStrap参见源码第15章),那么还是没有看到如何解析web.xml呢

解析web.xml在ContextConfig文件中,在start()方法中有

深入刨析tomcat 之---第11篇 how tomcat works( 第15章 ) 如何解析web.xml 文件

其中1是解析conf/web.xml文件的, 其2是解析web应用下的web.xml文件的

深入刨析tomcat 之---第11篇 how tomcat works( 第15章 ) 如何解析web.xml 文件

能看到使用一个Digester 去读取 jndi:/localhost/app1/WEB-INF/web.xml这个文件,新建一个Digester,重要的代码有

webDigester.push(context);

将context对象压入digester的内部栈,然后使用规则去解析web.xml文件,那么重要的是看解析规则,解析规则,在新建这个对象的时候添加的

深入刨析tomcat 之---第11篇 how tomcat works( 第15章 ) 如何解析web.xml 文件

看这个类中的方法

    public void addRuleInstances(Digester digester) {

        digester.addRule(prefix + "web-app",
                         new SetPublicIdRule(digester, "setPublicId"));

        digester.addCallMethod(prefix + "web-app/context-param",
                               "addParameter", 2);
        digester.addCallParam(prefix + "web-app/context-param/param-name", 0);
        digester.addCallParam(prefix + "web-app/context-param/param-value", 1);

        digester.addCallMethod(prefix + "web-app/display-name",
                               "setDisplayName", 0);

        digester.addRule(prefix + "web-app/distributable",
                         new SetDistributableRule(digester));

        digester.addObjectCreate(prefix + "web-app/ejb-local-ref",
                                 "org.apache.catalina.deploy.ContextLocalEjb");
        digester.addSetNext(prefix + "web-app/ejb-local-ref",
                            "addLocalEjb",
                            "org.apache.catalina.deploy.ContextLocalEjb");

        digester.addCallMethod(prefix + "web-app/ejb-local-ref/description",
                               "setDescription", 0);
        digester.addCallMethod(prefix + "web-app/ejb-local-ref/ejb-link",
                               "setLink", 0);
        digester.addCallMethod(prefix + "web-app/ejb-local-ref/ejb-ref-name",
                               "setName", 0);
        digester.addCallMethod(prefix + "web-app/ejb-local-ref/ejb-ref-type",
                               "setType", 0);
        digester.addCallMethod(prefix + "web-app/ejb-local-ref/local",
                               "setLocal", 0);
        digester.addCallMethod(prefix + "web-app/ejb-local-ref/local-home",
                               "setHome", 0);

        digester.addObjectCreate(prefix + "web-app/ejb-ref",
                                 "org.apache.catalina.deploy.ContextEjb");
        digester.addSetNext(prefix + "web-app/ejb-ref",
                            "addEjb",
                            "org.apache.catalina.deploy.ContextEjb");

        digester.addCallMethod(prefix + "web-app/ejb-ref/description",
                               "setDescription", 0);
        digester.addCallMethod(prefix + "web-app/ejb-ref/ejb-link",
                               "setLink", 0);
        digester.addCallMethod(prefix + "web-app/ejb-ref/ejb-ref-name",
                               "setName", 0);
        digester.addCallMethod(prefix + "web-app/ejb-ref/ejb-ref-type",
                               "setType", 0);
        digester.addCallMethod(prefix + "web-app/ejb-ref/home",
                               "setHome", 0);
        digester.addCallMethod(prefix + "web-app/ejb-ref/remote",
                               "setRemote", 0);

        digester.addObjectCreate(prefix + "web-app/env-entry",
                                 "org.apache.catalina.deploy.ContextEnvironment");
        digester.addSetNext(prefix + "web-app/env-entry",
                            "addEnvironment",
                            "org.apache.catalina.deploy.ContextEnvironment");

        digester.addCallMethod(prefix + "web-app/env-entry/description",
                               "setDescription", 0);
        digester.addCallMethod(prefix + "web-app/env-entry/env-entry-name",
                               "setName", 0);
        digester.addCallMethod(prefix + "web-app/env-entry/env-entry-type",
                               "setType", 0);
        digester.addCallMethod(prefix + "web-app/env-entry/env-entry-value",
                               "setValue", 0);

        digester.addObjectCreate(prefix + "web-app/error-page",
                                 "org.apache.catalina.deploy.ErrorPage");
        digester.addSetNext(prefix + "web-app/error-page",
                            "addErrorPage",
                            "org.apache.catalina.deploy.ErrorPage");

        digester.addCallMethod(prefix + "web-app/error-page/error-code",
                               "setErrorCode", 0);
        digester.addCallMethod(prefix + "web-app/error-page/exception-type",
                               "setExceptionType", 0);
        digester.addCallMethod(prefix + "web-app/error-page/location",
                               "setLocation", 0);

        digester.addObjectCreate(prefix + "web-app/filter",
                                 "org.apache.catalina.deploy.FilterDef");
        digester.addSetNext(prefix + "web-app/filter",
                            "addFilterDef",
                            "org.apache.catalina.deploy.FilterDef");

        digester.addCallMethod(prefix + "web-app/filter/description",
                               "setDescription", 0);
        digester.addCallMethod(prefix + "web-app/filter/display-name",
                               "setDisplayName", 0);
        digester.addCallMethod(prefix + "web-app/filter/filter-class",
                               "setFilterClass", 0);
        digester.addCallMethod(prefix + "web-app/filter/filter-name",
                               "setFilterName", 0);
        digester.addCallMethod(prefix + "web-app/filter/large-icon",
                               "setLargeIcon", 0);
        digester.addCallMethod(prefix + "web-app/filter/small-icon",
                               "setSmallIcon", 0);

        digester.addCallMethod(prefix + "web-app/filter/init-param",
                               "addInitParameter", 2);
        digester.addCallParam(prefix + "web-app/filter/init-param/param-name",
                              0);
        digester.addCallParam(prefix + "web-app/filter/init-param/param-value",
                              1);

        digester.addObjectCreate(prefix + "web-app/filter-mapping",
                                 "org.apache.catalina.deploy.FilterMap");
        digester.addSetNext(prefix + "web-app/filter-mapping",
                            "addFilterMap",
                            "org.apache.catalina.deploy.FilterMap");

        digester.addCallMethod(prefix + "web-app/filter-mapping/filter-name",
                               "setFilterName", 0);
        digester.addCallMethod(prefix + "web-app/filter-mapping/servlet-name",
                               "setServletName", 0);
        digester.addCallMethod(prefix + "web-app/filter-mapping/url-pattern",
                               "setURLPattern", 0);

        digester.addCallMethod(prefix + "web-app/listener/listener-class",
                               "addApplicationListener", 0);

        digester.addObjectCreate(prefix + "web-app/login-config",
                                 "org.apache.catalina.deploy.LoginConfig");
        digester.addSetNext(prefix + "web-app/login-config",
                            "setLoginConfig",
                            "org.apache.catalina.deploy.LoginConfig");

        digester.addCallMethod(prefix + "web-app/login-config/auth-method",
                               "setAuthMethod", 0);
        digester.addCallMethod(prefix + "web-app/login-config/realm-name",
                               "setRealmName", 0);
        digester.addCallMethod(prefix + "web-app/login-config/form-login-config/form-error-page",
                               "setErrorPage", 0);
        digester.addCallMethod(prefix + "web-app/login-config/form-login-config/form-login-page",
                               "setLoginPage", 0);

        digester.addCallMethod(prefix + "web-app/mime-mapping",
                               "addMimeMapping", 2);
        digester.addCallParam(prefix + "web-app/mime-mapping/extension", 0);
        digester.addCallParam(prefix + "web-app/mime-mapping/mime-type", 1);

        digester.addCallMethod(prefix + "web-app/resource-env-ref",
                               "addResourceEnvRef", 2);
        digester.addCallParam(prefix + "web-app/resource-env-ref/resource-env-ref-name", 0);
        digester.addCallParam(prefix + "web-app/resource-env-ref/resource-env-ref-type", 1);

        digester.addObjectCreate(prefix + "web-app/resource-ref",
                                 "org.apache.catalina.deploy.ContextResource");
        digester.addSetNext(prefix + "web-app/resource-ref",
                            "addResource",
                            "org.apache.catalina.deploy.ContextResource");

        digester.addCallMethod(prefix + "web-app/resource-ref/description",
                               "setDescription", 0);
        digester.addCallMethod(prefix + "web-app/resource-ref/res-auth",
                               "setAuth", 0);
        digester.addCallMethod(prefix + "web-app/resource-ref/res-ref-name",
                               "setName", 0);
        digester.addCallMethod(prefix + "web-app/resource-ref/res-sharing-scope",
                               "setScope", 0);
        digester.addCallMethod(prefix + "web-app/resource-ref/res-type",
                               "setType", 0);

        digester.addObjectCreate(prefix + "web-app/security-constraint",
                                 "org.apache.catalina.deploy.SecurityConstraint");
        digester.addSetNext(prefix + "web-app/security-constraint",
                            "addConstraint",
                            "org.apache.catalina.deploy.SecurityConstraint");

        digester.addRule(prefix + "web-app/security-constraint/auth-constraint",
                         new SetAuthConstraintRule(digester));
        digester.addCallMethod(prefix + "web-app/security-constraint/auth-constraint/role-name",
                               "addAuthRole", 0);
        digester.addCallMethod(prefix + "web-app/security-constraint/display-name",
                               "setDisplayName", 0);
        digester.addCallMethod(prefix + "web-app/security-constraint/user-data-constraint/transport-guarantee",
                               "setUserConstraint", 0);

        digester.addObjectCreate(prefix + "web-app/security-constraint/web-resource-collection",
                                 "org.apache.catalina.deploy.SecurityCollection");
        digester.addSetNext(prefix + "web-app/security-constraint/web-resource-collection",
                            "addCollection",
                            "org.apache.catalina.deploy.SecurityCollection");
        digester.addCallMethod(prefix + "web-app/security-constraint/web-resource-collection/http-method",
                               "addMethod", 0);
        digester.addCallMethod(prefix + "web-app/security-constraint/web-resource-collection/url-pattern",
                               "addPattern", 0);
        digester.addCallMethod(prefix + "web-app/security-constraint/web-resource-collection/web-resource-name",
                               "setName", 0);

        digester.addCallMethod(prefix + "web-app/security-role/role-name",
                               "addSecurityRole", 0);

        digester.addRule(prefix + "web-app/servlet",
                         new WrapperCreateRule(digester));
        digester.addSetNext(prefix + "web-app/servlet",
                            "addChild",
                            "org.apache.catalina.Container");

        digester.addCallMethod(prefix + "web-app/servlet/init-param",
                               "addInitParameter", 2);
        digester.addCallParam(prefix + "web-app/servlet/init-param/param-name",
                              0);
        digester.addCallParam(prefix + "web-app/servlet/init-param/param-value",
                              1);

        digester.addCallMethod(prefix + "web-app/servlet/jsp-file",
                               "setJspFile", 0);
        digester.addCallMethod(prefix + "web-app/servlet/load-on-startup",
                               "setLoadOnStartupString", 0);
        digester.addCallMethod(prefix + "web-app/servlet/run-as/role-name",
                               "setRunAs", 0);

        digester.addCallMethod(prefix + "web-app/servlet/security-role-ref",
                               "addSecurityReference", 2);
        digester.addCallParam(prefix + "web-app/servlet/security-role-ref/role-link", 1);
        digester.addCallParam(prefix + "web-app/servlet/security-role-ref/role-name", 0);

        digester.addCallMethod(prefix + "web-app/servlet/servlet-class",
                              "setServletClass", 0);
        digester.addCallMethod(prefix + "web-app/servlet/servlet-name",
                              "setName", 0);

        digester.addCallMethod(prefix + "web-app/servlet-mapping",
                               "addServletMapping", 2);
        digester.addCallParam(prefix + "web-app/servlet-mapping/servlet-name", 1);
        digester.addCallParam(prefix + "web-app/servlet-mapping/url-pattern", 0);

        digester.addCallMethod(prefix + "web-app/session-config/session-timeout",
                               "setSessionTimeout", 1,
                               new Class[] { Integer.TYPE });
        digester.addCallParam(prefix + "web-app/session-config/session-timeout", 0);

        digester.addCallMethod(prefix + "web-app/taglib",
                               "addTaglib", 2);
        digester.addCallParam(prefix + "web-app/taglib/taglib-location", 1);
        digester.addCallParam(prefix + "web-app/taglib/taglib-uri", 0);

        digester.addCallMethod(prefix + "web-app/welcome-file-list/welcome-file",
                               "addWelcomeFile", 0);

    }

对于规则分析如下

        digester.addRule(prefix + "web-app",
                         new SetPublicIdRule(digester, "setPublicId"));

深入刨析tomcat 之---第11篇 how tomcat works( 第15章 ) 如何解析web.xml 文件

没做别的就是调用了context的 setPublicId方法

对于没设计的规则不解读,那么找 <servlet> 标签

深入刨析tomcat 之---第11篇 how tomcat works( 第15章 ) 如何解析web.xml 文件

那么遇到web-app/servlet的第一规则是WrapperCreateRule

深入刨析tomcat 之---第11篇 how tomcat works( 第15章 ) 如何解析web.xml 文件

begin中执行了,peek出栈中的context对象(就是之前push进去的代表app1的standardContext对象,

那么新建一个wrapper对象,将wrapper对象 push的digest栈中,

接下的规则是

 digester.addSetNext(prefix + "web-app/servlet",
                            "addChild",
                            "org.apache.catalina.Container");

调用栈顶-1位置的对象的 addChild() 方法将栈顶对象作为参数,添加的context对象(栈顶-1位置对象)

接着遇到的标签是<servlet-name>Modern</servlet-name>

深入刨析tomcat 之---第11篇 how tomcat works( 第15章 ) 如何解析web.xml 文件

这就是调用wrapper对象的setname方法将参数 "Modern" 标签的值,放入的wrapper对象中

不就是调用了

Wrapper wrapper2 = new StandardWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet");

如下的语句吗? 

确实是,到此就分析完毕了,

总结: 以上涉及了俩个问题1, tomcat是如何给host对象配置web.xml规则的StandardContext的,就是看webapps文件下有几个web应用,给每个新建一个StandardContext

在新建一个digester将context压栈,接着按规则解析web.xml.解析过程无非就是三种,一个新建对象,2调用对象方法,3 建立栈顶元素和栈顶-1位置元素的关系(addChild方法)

第二个问题是:digester解析web.xml的过程

结束: