【tomcat路径匹配源码分析】搞懂tomcat中web.xml配置servlet的url-pattern为“/“和“/*“的区别

article/2025/9/15 10:02:01

搞懂tomcat中web.xml配置servlet的url-pattern为"/"和"/*"的区别

  • 前言
    • 结论
    • Servlet匹配规则(tomcat源码)
    • 分析
    • 举个例子
    • 路径配置为`/`导致拦截静态资源问题的解决方案
  • 总结


原文地址

前言


我在写原生javaWEB项目时,想通过注册一个servlet实现拦截所有请求由HandOutServlet统一分发,于是我就想到把urlPatterns设置为"/"
负责请求分发的servlet
但是这个会出现一个问题就是这个也会把 静态资源(html,css,js等) 的请求也拦截了,导致前端无法获取到正确的页面资源(解决方法在下面会提到),也正好我在配置过滤器时使用到另一个形式的url就是"/*",所以我想弄清楚这两个的区别是什么?我于是开始了源码的调试和研究。。。。。。

结论


<url-pattern>/</url-pattern>:

url-pattern为"/"时会覆盖tomcat中的default servlet(这个给是用于提供静态资源的),表示匹配所有没有注册的url-pattern的地址,就是web.xml里的其他的url-pattern都没有匹配时就会走这个地址。(比如index.html也会被匹配到因为没有注册html类型的mapping,而index.jsp就不会匹配因为jsp有注册了对应的servlet-mapping)

注意: 在tomcat的安装路径下也有一个web.xml(比如:“apache-tomcat-x.x.xx\conf\web.xml”),这个web.xml会和我们项目里的web.xml合并,该web.xml配置了默认的servlet和处理jsp的servlet

tomcat安装路径下的web.xml文件部分截图如下:
web.xml


<url-pattern>/*</url-pattern>:

url-pattern为"/*"时,其实和<url-pattern>/</url-pattern>都表示匹配所有资源,只不过<url-pattern>/*</url-pattern>先于<url-pattern>/</url-pattern>匹配,意思就是/*的优先级比/高。在项目的servlet中如果配置了<url-pattern>/*</url-pattern>,在没有配置处理对应静态资源的servlet时,什么index.jsp,index.html,css,js啥的文件统统匹配到/*对应的servlet下,不过如果注册有更加清晰的路径比如<url-pattern>/test/*</url-pattern>,请求路径如果是\test\123之类的则是匹配到<url-pattern>/test/*</url-pattern>这个,而不是<url-pattern>/*</url-pattern>,具体原因可以看下面的源码和解读


Servlet匹配规则(tomcat源码)

 private final void internalMapWrapper(ContextVersion contextVersion,CharChunk path,MappingData mappingData) throws IOException {int pathOffset = path.getOffset();int pathEnd = path.getEnd();boolean noServletPath = false;int length = contextVersion.path.length();if (length == (pathEnd - pathOffset)) {noServletPath = true;}int servletPath = pathOffset + length;path.setOffset(servletPath);// Rule 1 -- Exact MatchMappedWrapper[] exactWrappers = contextVersion.exactWrappers;internalMapExactWrapper(exactWrappers, path, mappingData);// Rule 2 -- Prefix Matchboolean checkJspWelcomeFiles = false;MappedWrapper[] wildcardWrappers = contextVersion.wildcardWrappers;if (mappingData.wrapper == null) {internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,path, mappingData);if (mappingData.wrapper != null && mappingData.jspWildCard) {char[] buf = path.getBuffer();if (buf[pathEnd - 1] == '/') {/** Path ending in '/' was mapped to JSP servlet based on* wildcard match (e.g., as specified in url-pattern of a* jsp-property-group.* Force the context's welcome files, which are interpreted* as JSP files (since they match the url-pattern), to be* considered. See Bugzilla 27664.*/mappingData.wrapper = null;checkJspWelcomeFiles = true;} else {// See Bugzilla 27704mappingData.wrapperPath.setChars(buf, path.getStart(),path.getLength());mappingData.pathInfo.recycle();}}}if(mappingData.wrapper == null && noServletPath &&contextVersion.object.getMapperContextRootRedirectEnabled()) {// The path is empty, redirect to "/"path.append('/');pathEnd = path.getEnd();mappingData.redirectPath.setChars(path.getBuffer(), pathOffset, pathEnd - pathOffset);path.setEnd(pathEnd - 1);return;}// Rule 3 -- Extension MatchMappedWrapper[] 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++) {path.setOffset(pathOffset);path.setEnd(pathEnd);path.append(contextVersion.welcomeResources[i], 0,contextVersion.welcomeResources[i].length());path.setOffset(servletPath);// 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) {String pathStr = path.toString();WebResource file =contextVersion.resources.getResource(pathStr);if (file != null && file.isFile()) {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());mappingData.matchType = ApplicationMappingMatch.DEFAULT;}// Redirection to a folderchar[] buf = path.getBuffer();if (contextVersion.resources != null && buf[pathEnd -1 ] != '/') {String pathStr = path.toString();// Note: Check redirect first to save unnecessary getResource()//       call. See BZ 62968.if (contextVersion.object.getMapperDirectoryRedirectEnabled()) {WebResource file;// Handle context rootif (pathStr.length() == 0) {file = contextVersion.resources.getResource("/");} else {file = contextVersion.resources.getResource(pathStr);}if (file != null && file.isDirectory()) {// Note: this mutates the path: do not do any processing// after this (since we set the redirectPath, there// shouldn't be any)path.setOffset(pathOffset);path.append('/');mappingData.redirectPath.setChars(path.getBuffer(), path.getStart(), path.getLength());} else {mappingData.requestPath.setString(pathStr);mappingData.wrapperPath.setString(pathStr);}} else {mappingData.requestPath.setString(pathStr);mappingData.wrapperPath.setString(pathStr);}}}path.setOffset(pathOffset);path.setEnd(pathEnd);}

分析

从源码结合注释可以看出servlet的匹配规则顺序是:

  1. 精确匹配:查找一个与请求路径匹配的 Servlet(比如:请求路径是/test/login,如果配置有<url-pattern>/test/login</url-pattern>的servlet映射则会匹配该servlet,没有接着下一个规则即前缀路径匹配,下面的规则也是如此,没有匹配到就到下一个规则,直到默认的处理)
  2. 前缀路径匹配:通配符匹配,匹配前缀最长的(比如<url-pattern>/test1/test2/*</url-pattern><url-pattern>/test1/*</url-pattern>,请求路径假设是/test/login/123则匹配的是<url-pattern>/test1/test2/*</url-pattern>,而不是<url-pattern>/test1/*</url-pattern>
  3. 扩展名匹配:如果请求路径最后一部分包含扩展名,就像 .html之类的,则尝试匹配注册有处理此扩展名请求的 Servlet(比如:<url-pattern>*.html</url-pattern>
  4. 欢迎资源处理:查找是否有匹配的默认首页文件(在请求路径后加上欢迎文件列表比如请求的是/test,则添加后就是/test/index.html,欢迎列表默认有[index.html,index.htm,index.jsp],这些都会依次尝试匹配)
    下图就是tomcat默认的欢迎列表:
    默认欢迎列表
  5. 默认匹配:前几个规则没有匹配成功,交由默认 Servlet处理,如果在自己项目的web.xml配置有<url-pattern>/</url-pattern>则这个就是默认的servlet,即它取代了tomcat默认的servlet

举个例子

假设项目结构如下(故意加了一个index.jsp)
项目结构
项目的上下文是/test
tomcat配置
在浏览器访问localhost:8080/test
浏览器访问

之后会被重定向到localhost:8080/test/
在这里插入图片描述

假设没有在项目的web.xml中配置任何servlet(但是不要忘记了还有tomcat默认的servlet)
web.xml配置

http://localhost:8080/test/这个请求进来,经过处理实际进行匹配的路径是/,然后开始第一个匹配规则精确匹配,这时候因为在web.xml中没有配置有明确路径的servlet,所以第一个匹配失败,接着尝试前缀匹配,和第一个规则一样,没有配置这类的servlet所以也失败了,然后是拓展名匹配,默认tomcat的web.xml配置有处理拓展名为jspjspx的servlet,但是这个也不符合/这个路径;接着下一个规则就是欢迎资源处理,这里会给路径加上欢迎列表中指定的文件。

默认的欢迎列表如下
默认欢迎列表
即给路径/加上index.html变成/index.html,之后欢迎列表会按顺序依此在路径上添加后进行尝试匹配,首先开始尝试/index.html

  1. 一开始先进行精确匹配,和一开始一样,因为在web.xml中没有配置有明确路径的servlet,所以匹配失败
  2. 之后开始前缀匹配,这个也和之前一样,没有配置这类的servlet所以也失败了
  3. 在尝试过加上欢迎列表后的精确匹配和前缀匹配后,如果还没找到对应的处理的serlvet,会判断项目物理上是否存在欢迎列表中指定的文件即index.html如果物理上存在在且注册有对应的拓展名servlet映射,就会交给该servlet处理,如果没有对应的拓展名映射的servlet就会交给默认的servlet处理。显然index.html物理上是存在的,但是我们没有配置处理html拓展名的servlet,所以会交给默认的servlet处理,之后就不会继续尝试/index.htm/index.jsp了。默认的servlet是处理静态资源的所以可以正确响应index.html文件。

响应后浏览器显示
index.html

路径配置为/导致拦截静态资源问题的解决方案

这个是我的项目结构
项目webapp结构

static包下放了css,js,image等资源

我的 web.xml 的部分配置

   <servlet-mapping><servlet-name>default</servlet-name><url-pattern>*.html</url-pattern></servlet-mapping><servlet-mapping><servlet-name>default</servlet-name><url-pattern>/static/*</url-pattern></servlet-mapping>

这样配置映射可以将html文件和static包下的其他的静态资源的请求交给tomcat默认的servlet处理

总结

以上就是我对servlet路径匹配的一些见解

喜欢这篇文章就点个赞呗!!!



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

相关文章

通过URL请求tomcat服务器直接下载文件

从一个服务器上下载文件&#xff0c;是很常见的情况。通常我们想通过URL来访问直接下载一个文件&#xff0c;对于Tomcat服务器而言&#xff0c;是非常简单的&#xff0c;Tomcat本身就是作为一个web服务器的&#xff0c;通过简单的配置就可以实现。 配置如下&#xff1a; 1、在…

tomcat配置使得访问http协议(或者直接输入www.网址.com) 直接跳转访问https协议

由于https协议比http协议安全,但是如果网站安装了ssl(如果没有安装,可百度搜索"阿里云ssl免费证书",阿里官网会提供安装方法,比较简单),发现每次输入网址必须得手动输入https,而且大家习惯输入www.网址.com,会直接导致访问不到网站的.下面介绍直接修改tomcat配置,使得…

URL请求省略端口号和项目名访问Tomcat部署的项目

前言&#xff1a;很多时候Tomcat部署好Web项目之后访问项目的时候都需要加上端口号和项目名&#xff0c;如&#xff1a;http://localhost:8080/项目名&#xff0c;下面我会介绍可以不用输端口号和项目名便可以直接进行访问的方法&#xff0c;其实就是修改Tomcat下conf目录中的s…

让tomcat服务器使用url rewrite

第一步&#xff1a;首先到这里下载 http://tuckey.org/urlrewrite/ 稳定版&#xff1a;urlrewrite-2.6.0.jar 第二步&#xff1a;将urlrewrite-2.6.0.jar 放到tomcat的lib目录下。 第三步&#xff1a;将urlrewrite-2.6.0.jar 放到Netbeans的工程目录下的…

Tomcat之中文URL问题

背景 如果你需要访问带有中文路径的URL怎么办&#xff1f;比如说mp3、mp4文件。 比如这样的路径&#xff1a; http://127.0.0.1:8080/LZPlayer/mp3/1168/徐小凤 - 顺流逆流.mp3 那么&#xff0c;需要去修改Tomcat的配置文件&#xff0c;让他支持中文即可。 步骤 修改配置文…

URL访问地址和Tomcat项目部署中不得不说的小秘密

今天来简单讲讲tomcat项目部署和url地址访问栏的关系&#xff0c;顺便和大家分享下&#xff0c;叙述不当之处&#xff0c;还请大家多多指导&#xff01; 众所周知&#xff0c;Tomcat项目发布的默认访问地址格式如下&#xff1a;http://localhost:8080/MyDemo/index.jsp 由于we…

opencv——边缘检测算法(总结)

前言 笔记。 一、边缘检测算法 边缘检测算法是指利用灰度值的不连续性质&#xff0c;以灰度突变为基础分割出目标区域。对铝铸件表面进行成像后会产生一些带缺陷的区域&#xff0c;这些区域的灰度值比较低&#xff0c;与背景图像相比在灰度上会有突变&#xff0c;这是由于这…

用OpenCV的边缘检测

使用OpenCV的边缘检测 代码如下&#xff1a; #include "infer.h"using namespace cv; using namespace std;int main::getResult(string fileName) {Mat src imread(fileName);Mat dst;cvtColor(src, src, COLOR_BGR2GRAY);blur(src, src, Size(3, 3));Canny(src,…

基于opencv的边缘检测方法

1、梯度运算 用OpenCV的形态变换&#xff08; 膨胀、腐蚀、开运算和闭运算&#xff09;函数morphologyEx 梯度运算即膨胀结果-腐蚀结果&#xff1a; 【注意】对于二值图像来说&#xff0c;必须是前景图像为白色&#xff0c;背景为黑色&#xff0c;否则需要进行反二值化处理 …

OpenCV——Canny边缘检测

目录 简介 实现步骤 代码 原图 效果图 简介 Canny边缘检测是一种使用多级边缘检测算法检测边缘的方法。 实现步骤 step1&#xff1a;去噪。噪声会影响边缘检测的准确性&#xff0c;因此首先要将噪声过滤掉。 方法&#xff1a;图像边缘容易受到噪声的干扰&#xff0c;因此…

OpenCV——边缘检测原理

边缘检测原理 图像的边缘指的是图像中像素灰度值突然发生变化的区域&#xff0c;如果将图像的每一行像素和每一列像素都描述成一个关于灰度值的函数&#xff0c;那么图像的边缘对应在灰度值函数中是函数值突然变大的区域。函数值的变化趋势可以用函数的导数描述。当函数值突然…

OpenCV 边缘检测之Canny算法(代码应用)

Canny算法 Canny是边缘检测算法&#xff0c;在1986年提出的。 是一个很好的边缘检测器 很常用也很实用的图像处理方法 Canny算法步骤 消除噪声&#xff1a;高斯模糊 - GaussianBlur灰度转换 - cvtColor计算梯度 – Sobel/Scharr非最大信号抑制高低阈值输出二值图像 API&…

opencv边缘检测加提取(圆形和矩形)

因为加密了的原因&#xff0c;不好直接复制&#xff0c;大家看一下代码就好。

OpenCV之边缘检测

1.图片的高斯模糊 import cv2 import matplotlib.pyplot as pltimg cv2.imread(r"C:\Users\Curry\Desktop\goutou.png") gray cv2.cvtColor(img, codecv2.COLOR_BGR2GRAY) gray2 cv2.GaussianBlur(gray,(19,19),0) #高斯模糊 # canny cv2.Canny()cv2.imshow(gra…

OpenCV如何进行图像的边缘检测?OpenCV边缘检测操作流程

OpenCV提供了几种常见的图像边缘检测算法&#xff0c;包括Sobel算子、Scharr算子、Laplacian算子和Canny边缘检测算法。下面分别介绍这些算法及其实现方法。 Sobel算子 Sobel算子是一种常见的图像边缘检测算法&#xff0c;其原理是通过对图像进行卷积操作&#xff0c;计算出图像…

opencv-边缘检测

文章目录 一、Canny边缘检测1.1高斯滤波器2.1梯度和方向3.1非极大值抑制4.1双阈值检测 一、Canny边缘检测 Canny边缘检测器是一种被广泛使用的算法&#xff0c;并被认为是边缘检测最优的算法&#xff0c;该方法使用了比高斯差分算法更复杂的技巧&#xff0c;如多向灰度梯度和滞…

opencv边缘检测算子

实验三 边缘检测算子 一、 实验目的 利用opencv或其他工具编写实现下图的sobel算子和robert算子边缘检测 二、 实验过程 利用opencv python实现sobel算子和robert算子边缘检测 (1)在python安装opencv库 这个步骤我在第二个实验&#xff0c;图像滤波里写过了&#xff0c;就不…

OpenCV:边缘检测算法

边缘检测&#xff08;英语&#xff1a;Edge detection&#xff09;是图像处理和计算机视觉中的基本问题&#xff0c;边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。这些包括&#xff08;i&#xff09;深度上的不连续、…

opencv边缘检测 roberts算子

定义roberts两个算子 分别为135度 和45度。 [1,0, 0,-1] [0,1 -1,0] 这里展示在c实现的过程。先展示下效果图 分别是45度角 135度角和边缘效果图。 #include<opencv2/opencv.hpp> #include<opencv2/core/core.hpp> #include<opencv2/imgproc/imgproc.hpp> #…

基于QT+OpenCV边缘检测、面积检测

没什么好说的就是调库&#xff0c;直接看下效果图&#xff0c;&#xff0c;基于OPENC4.5.1&#xff0c;如需要请 原图&#xff1a; 对图形进行描边并进行标号 面积检测