web.xml中的url

前言

Servlet和filter是J2EE开发中常用的技术,使用方便,配置简单.但url-pattern可能有点迷糊.这里总结其中url-pattern中的映射规则.

servlet容器对url的匹配过程

当一个请求发送到servlet容器的时候,容器先会将请求的url减去当前应用上下文的路径作为servlet的映射url,比如我访问的是 http://localhost/test/aaa.html,我的应用上下文是test,容器会将http://localhost/test去掉, 剩下的/aaa.html部分拿来做servlet的映射匹配。这个映射匹配过程是有顺序的(按照servlet-mapping在web.xml中声明的先后顺序),而且当有一个servlet匹配成功以后,就不会去理会剩下 的servlet了(filter不同,将符合条件都进行过滤)。

源码分析

有兴趣的其实可以研究一下源码, 就不需要来背这里总结写好的结论了, 建议大家多看看源码.这里参考的源码是`apache-tomcat-9.0.0.M6-src`.

想要了解url-pattern的大致配置必须了解org.apache.tomcat.util.http.mapper.Mapper这个类.

这个类上的源码注释:Mapper, which implements the servlet API mapping rules (which are derived from the HTTP rules). 意思也就是说 “Mapper是一个衍生自HTTP规则并实现了servlet API映射规则的类”。

tomcat在启动的时候会扫描web.xml文件, org.apache.tomcat.util.descriptor.web.WebXml这个类是扫描web.xml文件的, 然后得到的servlet的映射数据servletMappings(HashMap), map的key: url-pattern value: servlet-name
然后会调用Context(实现类为StandardContext)的addServletMapping方法。 这个方法会调用本文上面提到的Mapper的addWrapper方法.
这个方法进行if-else判断
1. 以 /* 结尾的。 path.endsWith(“/*”)
2. 以 *. 开头的。 path.startsWith(“*.”)
3. 是否是 /, path.equals(“/”)
4. 以上3种之外的。

各种对应的处理完成之后,会存入context的各种wrapper中。这里的context是ContextVersion,这是一个定义在Mapper内部的静态类。
它有4种wrapper。 defaultWrapper,exactWrapper, wildcardWrappers,extensionWrappers。

这里的Wrapper概念:

Wrapper 代表一个 Servlet,它负责管理一个 Servlet,包括的 Servlet 的装载、初始化、执行以及资源回收。

回过头来看mapper的addWrapper方法:
1. 我们看到 /* 对应的Servlet会被丢到wildcardWrappers中
2. *. 会被丢到extensionWrappers中
3. / 会被丢到defaultWrapper中
4. 其他的映射都被丢到exactWrappers中

用户请求过来的时候会调用mapper的internalMapWrapper方法, 方法体中代码的作者已经下好了注释, 用户请求这里进行URL匹配的时候是有优先级的.

// Rule 1 -- Exact Match        Wrapper[] exactWrappers = contextVersion.exactWrappers;        internalMapExactWrapper(exactWrappers, path, mappingData);        // Rule 2 -- Prefix Match        boolean checkJspWelcomeFiles = false;        Wrapper[] wildcardWrappers = contextVersion.wildcardWrappers;        if (mappingData.wrapper == null) {            internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,                                       path, mappingData);            .....        }        ....// Rule 3 -- Extension Match        Wrapper[] extensionWrappers = contextVersion.extensionWrappers;        if (mappingData.wrapper == null && !checkJspWelcomeFiles) {            internalMapExtensionWrapper(extensionWrappers, path, mappingData,                    true);        }        // Rule 4 -- Welcome resources processing for servlets        if (mappingData.wrapper == null) {            boolean checkWelcomeFiles = checkJspWelcomeFiles;            if (!checkWelcomeFiles) {                char[] buf = path.getBuffer();                checkWelcomeFiles = (buf[pathEnd - 1] == '/');            }            if (checkWelcomeFiles) {                for (int i = 0; (i < contextVersion.welcomeResources.length)                         && (mappingData.wrapper == null); i++) {                    ...// Rule 4a -- Welcome resources processing for exact macth                    internalMapExactWrapper(exactWrappers, path, mappingData);                    // Rule 4b -- Welcome resources processing for prefix match                    if (mappingData.wrapper == null) {                        internalMapWildcardWrapper                            (wildcardWrappers, contextVersion.nesting,                             path, mappingData);                    }                    // Rule 4c -- Welcome resources processing                    //            for physical folder                    if (mappingData.wrapper == null                        && contextVersion.resources != null) {                        Object file = null;                        String pathStr = path.toString();                        try {                            file = contextVersion.resources.lookup(pathStr);                        } catch(NamingException nex) {                            // Swallow not found, since this is normal                        }                        if (file != null && !(file instanceof DirContext) ) {                            internalMapExtensionWrapper(extensionWrappers, path,                                                        mappingData, true);                            if (mappingData.wrapper == null                                && contextVersion.defaultWrapper != null) {                                mappingData.wrapper =                                    contextVersion.defaultWrapper.object;                                mappingData.requestPath.setChars                                    (path.getBuffer(), path.getStart(),                                     path.getLength());                                mappingData.wrapperPath.setChars                                    (path.getBuffer(), path.getStart(),                                     path.getLength());                                mappingData.requestPath.setString(pathStr);                                mappingData.wrapperPath.setString(pathStr);                            }                        }                    }                }                path.setOffset(servletPath);                path.setEnd(pathEnd);            }        }        /* welcome file processing - take 2         * Now that we have looked for welcome files with a physical         * backing, now look for an extension mapping listed         * but may not have a physical backing to it. This is for         * the case of index.jsf, index.do, etc.         * A watered down version of rule 4         */        if (mappingData.wrapper == null) {            boolean checkWelcomeFiles = checkJspWelcomeFiles;            if (!checkWelcomeFiles) {                char[] buf = path.getBuffer();                checkWelcomeFiles = (buf[pathEnd - 1] == '/');            }            if (checkWelcomeFiles) {                for (int i = 0; (i < contextVersion.welcomeResources.length)                         && (mappingData.wrapper == null); i++) {                    path.setOffset(pathOffset);                    path.setEnd(pathEnd);                    path.append(contextVersion.welcomeResources[i], 0,                                contextVersion.welcomeResources[i].length());                    path.setOffset(servletPath);                    internalMapExtensionWrapper(extensionWrappers, path,                                                mappingData, false);                }                path.setOffset(servletPath);                path.setEnd(pathEnd);            }        }        // Rule 7 -- Default servlet        if (mappingData.wrapper == null && !checkJspWelcomeFiles) {            if (contextVersion.defaultWrapper != null) {                mappingData.wrapper = contextVersion.defaultWrapper.object;                mappingData.requestPath.setChars                    (path.getBuffer(), path.getStart(), path.getLength());                mappingData.wrapperPath.setChars                    (path.getBuffer(), path.getStart(), path.getLength());            }            ...        }

规则1:精确匹配,使用contextVersion的exactWrappers
规则2:前缀匹配,使用contextVersion的wildcardWrappers
规则3:扩展名匹配,使用contextVersion的extensionWrappers
规则4:使用资源文件来处理servlet,使用contextVersion的welcomeResources属性,这个属性是个字符串数组(如默认首页index.jsp)
规则7:使用默认的servlet,使用contextVersion的defaultWrapper

匹配规则和顺序如下:

精确路径匹配
比如servletA 的url-pattern为 /test,servletB的url-pattern为 /* ,这个时候,如果我访问的url为 http://localhost/test ,这个时候容器就会先进行精确路径匹配,发现/test正好被servletA精确匹配,那么就去调用servletA,也不会去理会其他的 servlet了
最长路径匹配
例子:servletA的url-pattern为 /test/*,而servletB的url-pattern为 /test/a/*,此时访问 http://localhost/test/a时,容器会选择路径最长的servlet来匹配,也就是这里的servletB
扩展匹配
如果url最后一段包含扩展(如 *.do),容器将会根据扩展选择合适的servlet。

注意:如果前面三条规则都没有找到一个servlet,容器会根据url选择对应的请求资源。如果应用定义了一个default servlet,则容器会将请求丢给default servlet(什么是default servlet?后面会讲)。

url-pattern详解

在web.xml文件中,以下语法用于定义映射:

  1. 以”/’开头和以”/*”结尾的是用来做路径映射的。
  2. 以前缀”*.”开头的是用来做扩展映射的。
  3. “/” 是用来定义default servlet映射的。
  4. 剩下的都是用来定义详细映射的。比如: /aa/bb/cc.action

注意事项
1. 容器会首先查找精确路径匹配,如果找不到,再查找目录匹配,如果也找不到,就查找扩展名匹配
2. 如果一个请求匹配多个“目录匹配”,容器会选择最长的匹配(也就是更为精确的路径)
3. 定义”/*.action”这样一个看起来很正常的匹配会报错?因为这个匹配即属于路径映射,也属于扩展映射,导致容器无法判断

自定义路径映射

我想定义一个除了一种情况的所有url-pattern,比如除了 *.jsp的所有情况
似乎找不到一种 all but ×××的写法
但似乎可以用下面这种方法:

<filter>    <filter-name>LoginFilter</filter-name>    <filter-class>com.test.LoginFilter</filter-class>    <init-param>        <param-name>UrlRegx</param-name>        <param-value><!--你的正则表达式--></param-value>    </init-param></filter>

自己定义一个规则,在后台进行二次过滤

什么是default servlet ?

  1. web.xml中如果某个Servlet的映射路径仅仅为一个正斜杠(/),那么这个Servlet就成为当前Web应用程序的缺省Servlet。
  2. 凡是在web.xml文件中找不到匹配的元素的URL,它们的访问请求都将交给缺省Servlet处理,也就是说,缺省Servlet用于处理所有其他Servlet都不处理的访问请求
  3. 在tomcat的安装目录\conf\web.xml文件中,注册了一个名称为org.apache.catalina.servlets.DefaultServlet的Servlet,并将这个Servlet设置为了缺省Servlet。(\conf\web.xml文件所有发布到tomcat的web应用所共享的)
     <servlet>        <servlet-name>default</servlet-name>        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>        <init-param>           <param-name>debug</param-name>           <param-value>0</param-value>        </init-param>        <init-param>           <param-name>listings</param-name>           <param-value>false</param-value>        </init-param>        <load-on-startup>1</load-on-startup>    </servlet>    <servlet-mapping>       <servlet-name>default</servlet-name>       <url-pattern>/</url-pattern>    </servlet-mapping>

4 . 当访问Tomcat服务器中的某个静态HTML文件和图片时,实际上是在访问这个缺省Servlet,由DefaultServlet类寻找,当寻找到了请求的html或图片时,则返回其资源文件,如果没有寻找到则报出404错误。

The default servlet is the servlet which serves static resources as
well as serves the directory listings (if directory listings are
enabled).
见官网http://tomcat.apache.org/tomcat-6.0-doc/default-servlet.html

5 . 如果在web应用的web.xml文件中的中配置了”/”,如:

    <servlet>      <servlet-name>ServletDemo3</servlet-name>      <servlet-class>edu.servlet.ServletDemo3</servlet-class>    </servlet>    <servlet-mapping>      <servlet-name>ServletDemo3</servlet-name>      <url-pattern>/</url-pattern>    </servlet-mapping>

则当请求的url和上面其他的均不匹配时,则会交给ServletDemo3.Java处理,而不在交给DefaultServlet.java处理,也就是说,当请求web应用中的静态资源等时,则全部进入了ServletDemo3.java,而不会正常返回页面资源。

参考文章: http://www.cnblogs.com/fangjian0423/p/servletContainer-tomcat-urlPattern.html

(0)

相关推荐