`
lxz891117
  • 浏览: 33371 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

Servlet容器Tomcat中web.xml中url-pattern的配置详解[附带源码分析]

 
阅读更多

http://www.cnblogs.com/fangjian0423/p/servletContainer-tomcat-urlPattern.html

前言

今天研究了一下tomcat上web.xml配置文件中url-pattern的问题。

这个问题其实毕业前就困扰着我,当时忙于找工作。 找到工作之后一直忙,也就没时间顾虑这个问题了。 说到底还是自己懒了,没花时间来研究。

今天看了tomcat的部分源码 了解了这个url-pattern的机制。 下面让我一一道来。

tomcat的大致结构就不说了, 毕竟自己也不是特别熟悉。 有兴趣的同学请自行查看相关资料。 等有时间了我会来补充这部分的知识的。

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

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

现象

首先先看我们定义的几个Servlet:

复制代码
    <servlet>
        <servlet-name>ExactServlet</servlet-name>
        <servlet-class>org.format.urlpattern.ExactServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ExactServlet</servlet-name>
        <url-pattern>/exact.do</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>ExactServlet2</servlet-name>
        <servlet-class>org.format.urlpattern.ExactServlet2</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ExactServlet2</servlet-name>
        <url-pattern>/exact2.do</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>TestAllServlet</servlet-name>
        <servlet-class>org.format.urlpattern.TestAllServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>TestAllServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>TestServlet</servlet-name>
        <servlet-class>org.format.urlpattern.TestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>TestServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
复制代码

有4个Servlet。 分别是2个精确地址的Servlet:ExactServlet和ExactServlet2。 1个urlPattern为 “/*” 的TestAllServlet,1个urlPattern为 "/" 的TestServlet。

我们先来看现象:

两个精确地址的Servlet都没问题。 找到并匹配了。


test.do这个地址并不存在,因为没有相应的精确的urlPattern。 所以tomcat选择urlPattern为 "/*" 的Servlet进行处理。

index.jsp(这个文件tomcat是存在的), 也被urlPattern为 "/*" 的Servlet进行处理。

我们发现,精确地址的urlPattern的优先级高于/*, "/" 规则的Servlet没被处理。

为什么呢? 开始分析源码。

源码分析

本次源码使用的tomcat版本是7.0.52.

tomcat在启动的时候会扫描web.xml文件。 WebXml这个类是扫描web.xml文件的,然后得到servlet的映射数据servletMappings。


然后会调用Context(实现类为StandardContext)的addServletMapping方法。 这个方法会调用本文开头提到的Mapper的addWrapper方法,这个方法在源码Mapper的360行。

这里,我们可以看到路径分成4类。

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中

最终debug看到的这些wrapper也验证了我们的结论。


这里多了2个扩展wrapper,tomcat默认给我们加入的,分别处理.jsp和.jspx。

好了。 在这之前都是tomcat启动的时候做的一些工作。

下面开始看用户请求的时候tomcat是如何工作的:

用户请求过来的时候会调用mapper的internalMapWrapper方法, Mapper源码830行。

复制代码
        // 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());
            }
            ...
        }
复制代码

这段代码作者已经为我们写好了注释.

Rule1,Rule2,Rule3....

看代码我们大致得出了:

用户请求这里进行url匹配的时候是有优先级的。 我们从上到下以优先级的高低进行说明:

规则1:精确匹配,使用contextVersion的exactWrappers

规则2:前缀匹配,使用contextVersion的wildcardWrappers

规则3:扩展名匹配,使用contextVersion的extensionWrappers

规则4:使用资源文件来处理servlet,使用contextVersion的welcomeResources属性,这个属性是个字符串数组

规则7:使用默认的servlet,使用contextVersion的defaultWrapper

最终匹配到的wrapper(其实也就是servlet)会被丢到MappingData中进行后续处理。

下面验证我们的结论:

我们在配置文件中去掉 /* 的TestAllServlet这个Servlet。 然后访问index.jsp。 这个时候规则1精确匹配没有找到,规则2前缀匹配由于去掉了TestAllServlet,因此为null,规则3扩展名匹配(tomcat自动为我们加入的处理.jsp和.jspx路径的)匹配成功。最后会输出index.jsp的内容。


验证成功。

我们再来验证http://localhost:7777/UrlPattern_Tomcat/地址。(TestAllServlet依旧不存在)


规则1,2前面已经说过,规则3是.jsp和.jspx。 规则4使用welcomeResources,这是个字符串数组,通过debug可以看到


会默认取这3个值。最终会通过规则4.c匹配成功,这部分大家可以自己查看源码分析。

最后我们再来验证一个例子:

将TestAllServlet的urlpattern改为/test/*。

地址 匹配规则情况 Servlet类
http://localhost:7777/UrlPattern_Tomcat/exact.do 规则1,精确匹配没有找到 ExactServlet
http://localhost:7777/UrlPattern_Tomcat/exact2.do 规则1,精确匹配没有找到 ExactServlet2
http://localhost:7777/UrlPattern_Tomcat/test/index.jsp 规则2,前缀匹配找到 TestAllServlet
http://localhost:7777/UrlPattern_Tomcat/index.jsp (规则2已经匹配到,这里没有进行匹配) 规则3,扩展名匹配没有找到 TestServlet


验证成功。

实战例子

SpringMVC相信大家基本都用过了。 还不清楚的同学可以看看它的入门blog:http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html

SpringMVC是使用DispatcherServlet做为主分发器的。 这个Servlet对应的url-pattern一般都会用“/”,当然用"/*"也是可以的,只是可能会有些别扭。

如果使用/*,本文已经分析过这个url-pattern除了精确地址,其他地址都由这个Servlet执行。

比如这个http://localhost:8888/SpringMVCDemo/index.jsp那么就会进入SpringMVC的DispatcherServlet中进行处理,最终没有没有匹配到 /index.jsp 这个 RequestMapping, 解决方法呢 就是配置一个:


最终没有跳到/webapp下的index.jsp页面,而是进入了SpringMVC配置的相应文件("/*"的优先级比.jsp高):


当然,这样有点别扭,毕竟SpringMVC支持RESTFUL风格的URL。

我们把url-pattern配置回 "/" 访问相同的地址, 结果返回的是相应的jsp页面("/"的优先级比.jsp低)。

总结

之前这个url-pattern的问题自己也上网搜过相关的结论,网上的基本都是结论,自己看过一次之后过段时间就忘记了。说到底还是不知道工作原理,只知道结论。而且刚好这方面的源码分析类型博客目前还未有人写过,于是这次自己也是决定看看源码一探究竟。

总结: 想要了解某一机制的工作原理,最好的方法就是查看源码。然而查看源码就需要了解大量的知识点,这需要花一定的时间。但是当你看明白了那些源码之后,工作原理也就相当熟悉了, 就不需要去背别人写好的一些结论了。 建议大家多看看源码。

文中难免有些错误,欢迎大家指出,

参考资料

http://www.ibm.com/developerworks/cn/java/j-lo-tomcat1/

https://www.ibm.com/developerworks/cn/java/j-lo-servlet/

分享到:
评论

相关推荐

    Tomcat中web.xml文件的详细说明

    Tomcat 中的 web.xml 文件是用于配置 Web 应用的核心文件,它定义了 Web 应用的基本信息、Filter、Servlet、Session 等信息。本文将详细介绍 web.xml 文件的结构和各个元素的作用。 1. 文件头部声明 web.xml 文件...

    web.xml配置文件详解

    它是Servlet容器(如Tomcat)读取Web应用程序配置信息的主要来源,因此深入理解其配置项及其作用至关重要。 #### 二、web.xml文件结构 `web.xml`文件的最外层标签为`&lt;web-app&gt;`,所有的配置项都需要定义在这个标签...

    Tomcat(二) Tomcat实现:Servlet与web.xml介绍 以及 源码分析Tomcat实现细节1

    【Servlet与web.xml详解】 Servlet是Java EE标准中的一部分,用于构建动态Web应用程序。Tomcat作为Servlet容器,它遵循Servlet规范来运行和管理Servlet。在Servlet中,有以下几个关键概念: 1. **Listener(监听器...

    tomcat配置文件web.xml与server.xml解析

    web.xml 配置文件是 Tomcat 服务器的主要配置文件,用于配置 Servlet、Servlet Mapping、Session 配置、MIME 类型映射和Welcome 文件列表等。 1. Servlet 配置 在 web.xml 文件中,Servlet 配置用于定义 Servlet ...

    web.xml详细说明

    `web.xml`是Java Web应用程序中的核心配置文件,用于描述和配置Web应用程序的各种属性、组件及其行为。本文档将详细解析`web.xml`中的各个元素及其功能,帮助开发者更好地理解和使用该文件。 #### 二、常用元素及其...

    Tomcat Web.xml 详解

    ### Tomcat Web.xml 详解 #### 一、概述 `web.xml` 文件是 Java Web 应用中的核心配置文件之一,它定义了应用程序的各种配置信息,包括但不限于 Servlet 的配置、过滤器(Filter)的配置、监听器(Listener)的...

    Tomcat web.xml 文件详解

    ### Tomcat web.xml 文件详解 #### 一、引言 `web.xml` 是部署在Tomcat服务器上的Web应用程序的核心配置文件之一。对于初次接触Tomcat部署的开发者而言,理解和掌握`web.xml`的配置至关重要。本文旨在深入解析`web...

    Tomcat web.xml,server.xml中详细配置说明文档

    本文档将深入探讨Tomcat中的两个核心配置文件:`web.xml`和`server.xml`,并提供详细的配置说明和实例。 ### 一、Tomcat介绍 1.1 **Tomcat简介** Tomcat是开源的Servlet容器,它实现了Java Servlet和JavaServer ...

    Web.xml配置文件.pdf

    `Web.xml`文件是Servlet容器(如Tomcat、Jetty)解析的配置文件,它为Web应用程序提供了元数据,包括Servlet、过滤器、监听器、会话配置、错误页面等。该文件位于`WEB-INF`目录下,是Java Web应用的标准配置部分。 ...

    java/jsp web.xml详解

    在Java和JSP开发中,`web.xml`文件是一个至关重要的组成部分,它是Servlet容器(如Tomcat)的部署描述符。这个文件包含了关于Web应用程序的配置信息,包括Servlet的定义、过滤器、监听器、会话配置、MIME类型映射、...

    web.xml配置文件详解[文].pdf

    `web.xml`是Java Web应用的核心配置文件,用于定义Servlet、过滤器、监听器以及其他相关组件的行为和配置。它是按照Servlet规范定义的标准格式编写,独立于任何特定的应用服务器,如Tomcat。以下是对`web.xml`文件中...

    jsp web.xml文件的作用及基本配置.docx

    - 对于 Tomcat 服务器而言,如果没有配置 `web.xml` 或者没有配置欢迎页,访问应用根目录时可能会出现默认页面或者错误提示。 - 但在大多数情况下,为了更好地管理和组织应用,建议保留并适当配置 `web.xml`。 ####...

    Web.xml常用元素

    `Web.xml`是Java Web应用程序中的核心配置文件,它用于配置应用程序的上下文信息、servlet映射、过滤器、监听器等关键组件。以下是对`Web.xml`中常见元素的详细解析: #### &lt;web-app&gt; - **作用**:根元素,所有...

    web.xml 2.4详细说明

    在Java Web开发中,`web.xml`文件是核心配置文件之一,它定义了应用程序的行为和结构,是Servlet容器(如Tomcat)运行Web应用程序的基础。本文将深入探讨`web.xml 2.4`版本中的关键节点及其详细说明,帮助开发者更好...

    web.xml配置详解

    在Java Web开发中,`web.xml`是Servlet容器(如Tomcat)用来解析和配置Web应用程序的核心文件,它被称为部署描述符。这个文件采用XML格式,包含了关于servlet、JSP应用的诸多配置信息,如servlet的注册、URL映射、...

    web.xml文件的作用及基本配置.pdf

    web.xml文件位于Web应用程序的WEB-INF目录下,是遵循Java EE规范的Web容器(如Apache Tomcat、Jetty等)识别的一个配置文件。它遵循特定的XML Schema,我们将在后文中提到它的具体Schema版本。 ### web.xml的基本...

    开发web_xml.rar_WEB XML_java web开发_web.xml_web开发_web

    在`web.xml`中,你可以定义Servlet,包括Servlet的类名、Servlet的URL映射以及初始化参数。例如: ```xml &lt;servlet&gt; &lt;servlet-name&gt;MyServlet&lt;/servlet-name&gt; &lt;servlet-class&gt;com.example.MyServlet&lt;/servlet-...

    servlet-api.jar 下载地址

    在Web应用中,`web.xml`文件是部署描述符,它指定了Servlet的映射、初始化参数和过滤器等配置。例如,你可以通过以下配置将一个Servlet映射到URL路径: ```xml &lt;servlet&gt; &lt;servlet-name&gt;MyServlet&lt;/servlet-name...

    用web.xml控制Web应用的行为

    - **命名JSP页面**:JSP页面通常通过`servlet-mapping`中的URL模式进行映射,但在`web.xml`中也可以直接定义,以便控制其访问。 4. **禁止激活器servlet** - **重新映射/servlet/URL模式**:默认情况下,servlet...

Global site tag (gtag.js) - Google Analytics