web.xml中url-pattern的配置详解

article/2025/10/2 17:41:36

前言

今天研究了一下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 derived from 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 MatchWrapper[] exactWrappers = contextVersion.exactWrappers;internalMapExactWrapper(exactWrappers, path, mappingData);// Rule 2 -- Prefix Matchboolean checkJspWelcomeFiles = false;Wrapper[] wildcardWrappers = contextVersion.wildcardWrappers;if (mappingData.wrapper == null) {internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,path, mappingData);.....}....// Rule 3 -- Extension MatchWrapper[] extensionWrappers = contextVersion.extensionWrappers;if (mappingData.wrapper == null && !checkJspWelcomeFiles) {internalMapExtensionWrapper(extensionWrappers, path, mappingData,true);}// Rule 4 -- Welcome resources processing for servletsif (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 macthinternalMapExactWrapper(exactWrappers, path, mappingData);// Rule 4b -- Welcome resources processing for prefix matchif (mappingData.wrapper == null) {internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,path, mappingData);}// Rule 4c -- Welcome resources processing//            for physical folderif (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 servletif (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低)。

参考资料

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

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

转发来源:https://www.cnblogs.com/fangjian0423/p/servletContainer-tomcat-urlPattern.html

http://chatgpt.dhexx.cn/article/tthq2pdV.shtml

相关文章

from django.urls.resolvers import URLPattern, URLResolver ImportError: cannot import name ‘URLPatter

from django.urls.resolvers import URLPattern, URLResolver ImportError: cannot import name URLPattern’错误解决办法 原因&#xff1a; django版本不支持 更改前&#xff1a; 更改后&#xff1a;

使用Servlet遇到的问题Caused by: java.lang.IllegalArgumentException: servlet映射中的<url pattern>[servlet]无效

Caused by: java.lang.IllegalArgumentException: servlet映射中的<url pattern>[servlet]无效 报这个错是因为web.xml中的url写的路径不对&#xff0c;应该在servlet前面加一个/

【WARNINGS: ?: (2_0.W001) Your URL pattern ‘^xxx/(?P<pk>\d+)/$‘ has a route that contains ‘(?P<‘, b】

项目场景&#xff1a; Django导入url路径 问题描述 WARNINGS: ?: (2_0.W001) Your URL pattern ‘^xxx/(?P\d)/KaTeX parse error: Expected group after ^ at position 52: …begins with a ^̲, or ends with…’. This was likely an oversight when migrating to djang…

Caused by: java.lang.IllegalArgumentException: servlet映射中的<url pattern>[servletDemo]无效

** Caused by: java.lang.IllegalArgumentException: servlet映射中的[servletDemo]无效 ** 原因是web.xml文件里面的url-pattern 地址没有配置好 少了 /

JavaWeb报错:servlet映射中的<url pattern>[login]无效

具体报错&#xff1a; Caused by: java.lang.IllegalStateException: 启动子级时出错... 34 moreCaused by: org.apache.catalina.LifecycleException: 无法启动组件... 42 moreCaused by: java.lang.IllegalArgumentException: servlet映射中的<url pattern>[login]无效…

URLpattern匹配规则

举例 urls.py&#xff1a; urlpatterns [# Examples:# url(r^$, PRSystem.views.home, namehome),# url(r^blog/, include(blog.urls)),url(r^admin/, include(admin.site.urls)),url(r^hello/$,hello), ] 简单来说&#xff0c;我们只是告诉 Django&#xff0c;所有指向 URL…

servlet映射中url pattern无效

<?xml version"1.0" encoding"UTF-8"?> <web-app xmlns"http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://xmlns.jcp.org/xml/ns/javaee http://…

servlet的url-pattern匹配规则

目录 1 url-pattern匹配规则说明 2 四种匹配规则优先顺序 2.1 精确匹配 2.2 路径匹配 2.3 扩展名匹配 2.4 缺省匹配 3 需要注意的问题 3.1 路径匹配和扩展名匹配无法同时设置 3.2 "/*"和"/"含义并不相同 3.3 tomcat默认配置的servlet 4 举例 5…

JavaWeb开发中,servlet的url-pattern的映射规则

原文出处&#xff1a;http://www.cnblogs.com/mailingfeng/archive/2012/04/05/2432687.html Servlet和filter是J2EE开发中常用的技术&#xff0c;使用方便&#xff0c;配置简单。servlet和filter中的url-pattern有一些文章在里面的&#xff0c;总结了一些东西&#xff0c;以免…

006_url-pattern配置

一. url-pattern配置 1. 在web.xml里面注册Servlet映射(servlet-mapping), 在Servlet里面我们需要配置url-pattern。url-pattern的作用就是在地址栏上的访问路径, 一定要以/打头。 2. 全路径匹配, 一级或者多级路径, 写法: /HWS或者/example/HWS。 请求路径: localhost:808…

Servlet学习(四):urlPattern配置与XML配置

1、urlPattern配置 Servlet类编写好后&#xff0c;要想被访问到&#xff0c;就需要配置其访问路径&#xff08;urlPattern&#xff09; 一个Servlet,可以配置多个urlPattern package com.itheima.web;import javax.servlet.ServletRequest; import javax.servlet.ServletRespo…

Makefile中变量的定义及赋值

1. 定义变量 Makefile 文件中定义变量的基本语法如下&#xff1a; 变量的名称值列表 Makefile 中的变量的使用其实非常的简单&#xff0c;因为它并没有像其它语言那样定义变量的时候需要使用数据类型。变量的名称可以由大小写字母、阿拉伯数字和下划线构成。等号左右的空白符…

makefile之.PHONY

1. 版本说明 首先说一下我用的make版本&#xff1a; GNU Make 4.2.1 2. 无PHONY无clean的情况&#xff08;无clean指当前目录下不存在clean文件&#xff09; 文件名&#xff1a;makefile (听说M必须大写&#xff0c;我用小写也可以执行成功) 文件内容&#xff1a; 且目录下…

Makefile文件:Makefile介绍

本文介绍Makefile的一些基本概念以及简单的用法。本文所用的编译器是Hightec tricore v4.9.1.0。 文章目录 1 Makefile的作用2 Makefile的规则3 一个简单的Makefile3.1 帮助文档中的例子3.2 例1&#xff1a;一个简单的Makefile 4 make是如何执行Makefile的4.1 默认目标4.2 例2…

Makefile入门详解

文章目录 一、Makefile简介二、makefile 原理1、当有依赖文件不存在2、当所有依赖文件存在 三、makefile基本规则1.makefile规则三要素2.基本规则3、makfile中的变量3.1普通变量3.2自动变量3.3例程 4、makefile中的伪目标 四、makefile其他常用的规则五、makefile中的函数5.1函…

makefile脚本

文章目录 1.makefile进行工程管理2.文档里面输入的内容3.如何运行这个makefile文件3.makefile 的变量4.其他 1.makefile进行工程管理 先创建一个名称为makefile或者Makefile的文档 2.文档里面输入的内容 输入相应内容的时候&#xff0c;要遵循相应的规则 规则&#xff1a;用…

makefile简介

1.make是一个应用程序 解析源程序之间的依赖关系 根据依赖关系自动维护编译工作 执行宿主操作系统中的各种命令 2.makefile是一个描述文件 定义一系列的规则来指定源文件编译的先后顺序 拥有特定的语法规则&#xff0c;支持函数定义和函数调用 能够直接集成操作系统中的各种命…

makefile变量

1.变量和不同的赋值方式 (1)makefile中支持程序设计语言中变量的概念 (2)makefile中的变量只代表文本数据(字符串) (3)makefile中的变量名规则 变量名可以包含字符&#xff0c;数字&#xff0c;下划线 不能包含“:”&#xff0c;"#"&#xff0c;"“或” " …

Makefile介绍

Makefile 是一种常用于编译的脚本语言&#xff0c;它可以更好更方便的管理你的项目的代码编译&#xff0c;节约编译时间&#xff08;没改动的文件不编译&#xff09;。 注意 Makefile 文件命令必须是 Makefile 或者 makefile&#xff0c;并使用 make 命令编译。 1. 1个…