从springmvc讨厌的404说起, 谈及tomcat的servlet路径匹配规则,/* 和 / 是不一样的hexo d!

缘起

提起springmvc,对于很多初学者而言, 很头痛的就是404问题. 其实本质上依旧是tomcat下的servlet的路径映射问题。 毕竟,springmvc的核心前端分发器就是DispatchServlet.

工程很简单. 具体代码参见 Demo链接【1】, 我使用tomcat容器运行此项目, 已经将上下文路径设定为 /, 将端口设置为80.

下面我们对此springmvc工程进行测试, 有

a. http://localhost/

b. http://localhost/js/my.js

c. http://localhost/index.jsp

三个地址. 项目的web.xml 中的 url-pattern依次为

  1. / , 则依次访问上述三个地址的结果为

    a. 浏览器打印 首页, 如果将index.jsp删除, 则将打印卧槽,尼玛!

    b. 报404, 控制台打印日志

    1
    警告: No mapping found for HTTP request with URI [/js/my.js] in DispatcherServlet with name 'springmvc'

    c. 浏览器打印首页, 如果将index.jsp删除的话, 显然404, 但是控制台没有打印任何日志. 表明就算是404,但也没有经过springmvc的DispatchServlet.

  2. /*, 则依次访问上述三个地址的结果均为404,并且控制台分别打印如下日志

    1
    警告: No mapping found for HTTP request with URI [/WEB-INF/content/welcome.jsp] in DispatcherServlet with name 'springmvc'
    1
    警告: No mapping found for HTTP request with URI [/js/my.js] in DispatcherServlet with name 'springmvc'
    1
    警告: No mapping found for HTTP request with URI [/index.jsp] in DispatcherServlet with name 'springmvc'
  3. *.jsp, 则依次访问上述三个地址的结果为

    a. 404, 控制台打印日志

    1
    警告: No mapping found for HTTP request with URI [/] in DispatcherServlet with name 'springmvc'

    b. 浏览器打印 alert("I am king")

    c. 404,控制台打印日志

    1
    警告: No mapping found for HTTP request with URI [/index.jsp] in DispatcherServlet with name 'springmvc'

分析

这是为什么呢? 学过springmvc的童鞋都知道, springmvc本质就是一个servlet. 即DispatchServlet. 所以一旦理解了tomcat中servlet的路径匹配原理的话, 上面的现象就都好解释了. 关于tomcat下的servlet、filter的4种路径匹配以及它们的优先级,参考【1】中的文章. 我只是简略表明和springmvc有关的观点

  1. /.jsp 和 / 分别属于路径匹配、扩展名匹配和缺省匹配,优先级递减.
  2. 缺省匹配/ tomcat本身就有设定(在$TOMCAT_HOME/conf/web.xml中就有设定),如果dispatchservlet也设定为/的话, 则将覆盖tomcat原本的缺省设定
  3. .jsp 在tomcat中本身亦有设定. 也在$TOMCAT_HOME/conf/web.xml中. 如果dispatchservlet设定\.jsp的匹配规则的话, 也将覆盖缺省设定.

有了上面3点认知,是不难理解上面的现象的. 根据1和3,我们就知道为什么springmvc设定/为拦截匹配规则的话, jsp其实是不会被拦截的. 因为优先级的缘故 会被tomcat的jsp-servlet处理. 我们也知道了 /* 是一种极为恶劣,极度不推荐的匹配规则(一般只有过滤器才会用). 因为它属于路径匹配,有着不错的优先级(仅次于精确匹配), 但是dispatchservlet拦截下来之后其实是没有能力对一些原本由tomcat处理的资源(一般是jsp以及一些诸如css、js、图片的静态资源)进行处理的. 导致找不到handlermapping 从而报404. 那么设定为 / 就好了吗? 其实也是一样的, 只是不会拦截jsp了而已(因为 / 的优先级比 扩展匹配低), 对于静态资源依旧会被拦截下来. 但是dispatchservlet并没有重复造轮子,所以并没有提供静态资源处理器,所以一个自然的想法就是这些静态资源交给tomcat原本的DefaultServlet处理. 所以就有了我们熟知的\<mvc: default-servlet-handler /> 或者 \<mvc: resources /> 配置. 目的就是将静态资源交给tomcat来处理. 譬如,一旦在 springmvc-config.xml 中加上

1
<mvc:default-servlet-handler />

则2.b 就不再是404,而是浏览器打印

1
alert("I am king")

参考

【1】https://blog.csdn.net/dream_mushuang/article/details/73743723

Demo

【1】https://github.com/yfsyfs/backend/tree/master/springmvc-learning