这篇文章主要讲解了“Servlet中/和/*的区别是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Servlet中/和/*的区别是什么”吧!
本文提纲
版本约定
✍正文
点拨“市面上”的错误答案
1、/用于Servlet,/*用于Filter
2、/不会匹配.jsp请求,而/*可以匹配到.jsp请求
3、/*匹配范围比/大
4、/匹配所有url(路径+后缀),/*只匹配路径型
Servlet四种匹配方式
1. 精确匹配
2. 路径匹配
3. 后缀名匹配
4. 缺省匹配
URL匹配注意事项
匹配顺序
/和/*的区别
DispatcherServlet不拦截.jsp请求根因分析
✍总结
JDK:8
Servlet:4.x
tomcat:9.x
什么样的答案终身难忘?学生时代关于记忆经常能听见两种论调:
死记硬背:见效快,但也忘得快,且一般不会灵活运用(指标不治本)
理解性记忆:见效慢,但记忆持久且会灵活运用(治标又治本)
如果是你,你愿意pick哪种?
正所谓授人以鱼不如授人以渔,后者方能形成永久记忆。不谋而合,本文将采用后种讲述方式,帮你记忆持久化。
关于/和/*的区别这个问题,依稀记得2015年我自学那会就能把它俩搞得明明白白,并且通过理解形成了“永久记忆”,所以至那会其就从来没有犯过迷糊,难道我就这么重视基础么(md,又在吹牛。。。)
如果用谷歌百度一下关键字:/和/*的区别
,搜索出来的答案不客气的说,基本全错!!! 错误的姿势基本还一模一样,原因你懂的。
各种错误case,且听我娓娓道来。搜集了下有如下4种主流答案,一一点拨。
环境说明:使用原生Servlet,war包方式部署至外置Tomcat作为服务器,端口号8080,context-path为:appcontext
反例:
@WebFilter(urlPatterns = {"/*"}) public class FakeServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("FakeServlet收到请求:" + req.getRequestURI()); } }
启动服务器,浏览器访问:http://localhost:8080/appcontext/api/demo1,控制台输出:
FakeServlet收到请求:/appcontext/api/demo1
一般来讲/
确实用于Servlet,/*
用于Filter,但并不代表这是正确的。
说明:Filter路径模式使用
/
无效
这个结论表面上看没有问题,但是往深了想一步,是否能够推导出这个结论:“/不会匹配.html请求,而/*可以匹配到.html请求”。试试看:
@WebServlet(urlPatterns = {"/"}) public class FakeServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("FakeServlet收到请求:" + req.getRequestURI()); } } @WebFilter(urlPatterns = {"/*"}) public class FakeFilter extends HttpFilter { @Override protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException { System.out.println("FakeFilter收到请求:" + req.getRequestURI()); super.doFilter(req, res, chain); } }
启动服务器,浏览器访问:http://localhost:8080/appcontext/api/demo1.jsp,控制台输出:
FakeFilter收到请求:/appcontext/api/demo1.jsp
servlet并未匹配上,似乎符合此结论:/不会匹配.jsp请求,而/*可以。
浏览器再访问:http://localhost:8080/appcontext/api/demo1.html
,控制台输出:
FakeFilter收到请求:/appcontext/api/demo1.html
FakeServlet收到请求:/appcontext/api/demo1.html
Filter和Servlet都匹配成功,破功了吧!
所以说,局限于该回答本身没有问题,而问题在于.jsp
后缀是一种特殊的请求,拿特殊案例当做通用结论肯定是站不住脚的。
通过本文下面的讲解你就会知道:/属于最大的的匹配范围,而/*
恰好是范围和/一样了而已,但/*
的优先级比/高,并不是它的匹配范围比/大。
用一句话反驳:/*
也能匹配上/api/demo1.html这种后缀型url(其实上面已经给出示例了)
这4个结论搜索排名非常靠前,不知误导了多少小朋友呀。与其每次将信将疑,倒不如花点时间写代码自己做个试验来得靠谱。我一向推崇的代码多动手,人云亦云不如自己来上一发。
带着这几个❌结论,接下来开始发大招啦:从根本上带你理解Servlet规范的URL匹配机制,从而理解到/
和/*
的区别,授之以渔让你终身难忘。
Servlet的urlPatterns路径映射
说明:本文所指的Servlet是广义的(规范),所以也包含Filter的urlPatterns
Servlet/Filter是服务端的一段小程序,用于处理Http请求。每个Servlet可以映射1个or多个路径,在xml时代这么写(url-pattern标签可写多个):
<servlet-mapping> <servlet-name>Demo1Servlet</servlet-name> <url-pattern>/api/demo1</url-pattern> <url-pattern>/api/demo2</url-pattern> </servlet-mapping>
@WebServlet注解方式这么写:
@WebServlet(urlPatterns = {"/api/demo1", "/api/demo2"}) public class Demo1Servlet extends HttpServlet { ... }
此时,该Servlet就能处理这两种 URL了。
问题来了,如果希望本Servlet处理某一类请求,该怎么破呢?
一类请求显然是无法一一枚举出来的,这时就需要用到Servlet的模式匹配了。urlPatterns除了写字面量的字符串,还支持pattern模式的字符串(从该属性的命名你应该也能看出来)。
接下来聚焦于Servlet的匹配方式展开详细讲解,这是本文的核心内容。
在Servlet规范中一共约定了四种匹配方式,无一例外,每种方式都非常重要和常用,下面逐一介绍。
顾名思义,urlPatterns是个无通配符的精确字符串,如:
@WebServlet(urlPatterns = {"/api/demo1", "/api/demo2"}) // 精确匹配 public class UrlPatternDemoServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.printf("收到请求:%s ServletPath:%s PathInfo:%s\n", req.getRequestURI(), req.getServletPath(), req.getPathInfo()); } }
打印里输出servletPath和pathInfo信息,让日志更具对比性
浏览器访问http://localhost:8080/appcontext/api/demo1
和/api/demo2
均能收到该请求,控制台分别打印:
收到请求:/appcontext/api/demo1 ServletPath:/api/demo1 PathInfo:null
收到请求:/appcontext/api/demo2 ServletPath:/api/demo2 PathInfo:null
pattern规则:以/
开头,且以/*
结尾。如:
@WebServlet(urlPatterns = {"/api/*", "/*"}) // 路径匹配 public class UrlPatternDemoServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 同上 } }
浏览器访问http://localhost:8080/appcontext/api/demo1
,控制台输出(匹配的/api/*
):
收到请求:/appcontext/api/demo1 ServletPath:/api PathInfo:/demo1
访问http://localhost:8080/appcontext/apiapi/demo1
,控制台输出(匹配的/*
:
收到请求:/appcontext/apiapi/demo1 ServletPath: PathInfo:/apiapi/demo1
关注点:当匹配上/*
模式时,ServletPath的值为空串,但PathInfo的值更为“丰富”了。
patten规则:以*.
开头(注意是开头,所以/api/*.jsp
这么写是非法的)。如:
@WebServlet(urlPatterns = {"*.jsp", "*.*"}) // 后缀名匹配 public class UrlPatternDemoServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 同上 } }
访问http://localhost:8080/appcontext/api/demo1
,结果404,因为没有后缀嘛;
访问http://localhost:8080/appcontext/api/demo1.jsp
,控制台输出(匹配*.jsp
):
收到请求:/appcontext/api/demo1.jsp ServletPath:/api/demo1.jsp PathInfo:null
访问http://localhost:8080/appcontext/api/demo1.servlet
,结果404,因为urlPatterns里没有匹配.servlet后缀的模式;
访问http://localhost:8080/appcontext/api/demo1.
,结果404,原因同上
访问http://localhost:8080/appcontext/api/demo1.*
,控制台打印(匹配*.*
):
收到请求:/appcontext/api/demo1.* ServletPath:/api/demo1.* PathInfo:null
发现没,这种匹配方式还蛮“特殊”的,需要注意这两点:
该模式以*.
开头,后面的均是常量,即使是*
也是常量。比如*.*
匹配的后缀必须是.*
而不能是其它
该匹配方式下,pathInfo永远是null,servletPath永远是“全部”
pattern规则:固定值/
。如:
想一想,这不就是我们熟悉的DispatcherServlet的匹配路径么?
@WebServlet(urlPatterns = "/") // 缺省匹配 public class UrlPatternDemoServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 同上 } }
这个时候匹配任意路径。
访问http://localhost:8080/appcontext,控制台打印:
收到请求:/appcontext/ ServletPath:/ PathInfo:null
访问http://localhost:8080/appcontext/api/demo1
,控制台打印:
收到请求:/appcontext/api/demo1 ServletPath:/api/demo1 PathInfo:null
访问http://localhost:8080/appcontext/api/demo1.html
,控制台打印:
收到请求:/appcontext/api/demo1.html ServletPath:/api/demo1.html PathInfo:null
此匹配规则下,pathInfo永远是null,servletPath永远是“全部”。
关于pathInfo:pathInfo只有当Servlet是路径匹配时,才有值。其它情况永远为null
Servlet对URL的匹配既不是Ant风格,也不是Regex。特殊符号只有单个的*
,且使用位置有强约束,切忌想当然的随意拼凑。
举例两种典型的错误理解,应该能帮助到你:
/api/*.jsp
:该urlPatterns是非法的,启动时会报错“IllegalArgumentException: servlet映射中的[/api/*.jsp]无效”。原因为:
若当路径匹配,/*
后面不能再有任何东西
若当后缀名匹配,*.
必须是最前面
/api/*/demo
:这个urlPatterns是合法的。只不过它属于精确匹配,也就是说别看它中间有*
,仍旧有且仅能匹配/api/*/demo
这个请求路径
有时候一个URL会被多个urlPatterns所匹配,这时谁优先呢?
Servlet同样遵循“国际惯例”:越精确越优先,越模糊越靠后。站在pattern模式的角度换句话讲就是:范围越小越优先,范围越大越靠后。
因此Servlet四种匹配方式顺序按范围从小到大(优先级从高到底)排序为:精确匹配 > 路径匹配 > 后缀名匹配 > 缺省匹配。
终于,来到了今天的主菜。
从上至下的阅读到这里,再看这个问题,是不是觉得答案已经浮出水面?那么,最后我还是来总结一下它俩的异同点:
相同点
绝大部分场景下具有相同的表现:匹配所有。
不同点
就是由于它们的相同点(如此相似),所以才让我们难以区分。
关于/
:
servlet中特殊的匹配模式(用在Filter中无效),
因为是缺省匹配代表匹配所有路径,所以只可能存在一个实例(若存在多个就覆盖)
优先级最低(兜底),这是和/*的最大区别。它不会覆盖任何其它的url-pattern,只会覆盖Servlet容器(如Tomcat)内建的DefaultServlet
关于/*
:
属于4中匹配模式中的路径匹配,可用于Servlet和Filter
优先级很高(仅次于精确匹配)。所以它会覆盖所有的后缀名匹配,从而很容易引起404问题,所以这种模式的“伤害性”是非常强的,一般有且仅用在Filter上
/
只能用于Servlet上,/*
一般只用于Filter上。
大家熟悉的Spring MVC的DispatcherServlet的匹配路径默认就是/
,它会拦截各种各样的请求,诸如下面这种请求都会拦截:
/api/demo1
/html/demo1.html
/static/main.js
但是,它不会拦截/api/demo1.jsp
这种以.jsp结尾的请求。据此现象就出现了:/
不拦.jsp请求而/*
拦截(/*
的范围比/
大)这种“错误”言论。
下面告诉你此现象的根因:Servlet容器(如Tomcat)内置有专门匹配.jsp
这种请求的Servlet处理器,如下图所示:
而后缀名匹配优先级高于缺省匹配,所以.jsp
结尾的请求不会被DispatcherServlet所“截胡”而是交给了JspServlet处理。
有了这波分析后,就问你,是不是就不用死记答案了?是不是就终身难忘啦?
Servlet的urlPatterns匹配方式是学习Java Web的重要一环,也是深入理解Spring MVC原理的大门,毕竟Spring MVC依旧是做业务开发的首选,而且还会持续很久、很久。
本文对Servlet的匹配方式做了全覆盖讲解,包括:
四种匹配方式匹配顺序(优先级)Servlet和Filter匹配的区别模式匹配中/
和/*
区别的根本原因
通过本文希望能让你不再被Servlet的模式匹配所困扰,更不要被一些似可非可的结论所迷惑,摇摆不定时大不了编码验证一下嘛。
感谢各位的阅读,以上就是“Servlet中/和/*的区别是什么”的内容了,经过本文的学习后,相信大家对Servlet中/和/*的区别是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。