你真的理解SpringMVC DispatcherServlet中的映射了吗?

故事扮演 提交于 2019-12-02 09:47:10

 

一、Request URL 与 Servlet url-pattern匹配顺序与关系

   当一个请求发送到servlet容器(服务器)的时候,容器先会将请求的url减去当前应用上下文的路径,就是scheme://ip:port/context url作为servlet的映射url,访问的是http://localhost:8080/test/index.html,我的应用上下文(context)是test,容器会将http://localhost:8080/test去掉,剩下的/index.html部分拿来和servleturl-pattern进行匹配。

1.        精确路径匹配(完全匹配)

”/”开始的,不包含通配符*(不以通配符结尾)的,例如:

  <servlet-mapping>

    <servlet-name>default</servlet-name>

    <url-pattern>/index.html</url-pattern>

  </servlet-mapping>

 

2.        最长路径匹配(路径匹配)

”/”开始的,并且以通配符*结尾的(通配符只能在结尾,不能放中间。例如,/index/te*/index.html这样的)

<servlet-mapping>

    <servlet-name>default</servlet-name>

    <url-pattern>/img/*</url-pattern>

 </servlet-mapping>

对于最长路径匹配,总是会匹配路径长的,例如,有2servlet-mapping如下。(本地服务器)

<servlet-mapping>

    <servlet-name>A</servlet-name>

    <url-pattern>/test/*</url-pattern>

  </servlet-mapping>

<servlet-mapping>

    <servlet-name>B</servlet-name>

    <url-pattern>/test/a/*</url-pattern>

  </servlet-mapping>

   对于请求http://localhost/test/a虽然AB都满足,但是servlet容器就会选择B,因为B匹配到的长度更长。

3.        扩展匹配

*.开始,以扩展名结束的,例如: 

 <servlet-mapping>

    <servlet-name>spring-dispatcher-servlet</servlet-name>

    <url-pattern>*.html</url-pattern>

  </servlet-mapping>

4.        默认servlet匹配

只有一个”/”,例如:

<servlet-mapping>

    <servlet-name>default</servlet-name>

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

  </servlet-mapping>

 

二、DispatcherServlet 的url-pattern

   在说DispatcherServlet url-pattern之前得先介绍一个比较重要的点,就是我们的项目下的WEB-INFweb.xml中的url-pattern是不允许重复的,例如同时在项目中配置如下2个相同默认的映射路径,服务器启动就会报错。

<servlet-mapping>

    <servlet-name>default</servlet-name>

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

  </servlet-mapping>

<servlet-mapping>

    <servlet-name>spring-dispatcher-servlet</servlet-name>

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

  </servlet-mapping>

   可能会有人有疑问,在tomcat服务器的web.xml中已经定义了如下图所示的servleturl-pattern,而在我们自己的项目中的web.xml中定义同样的默认servlet-mapping  url-pattern为什么没有报错呢?

图1  tomcat默认的Servlet

               

图2 tomcat jsp Servlet

                    

图3 tomcat 默认的映射

   对于这个问题还没有仔细研究过,不过应该是default servlet是单独的最后处理的,想一下这个道理很简单,只有我们的web.xml中没有找到映射才会到default servlet所以不存在冲突的问题。

下载再来看DispatcherServlet中下面这2servlet-mapping的区别

<servlet-mapping>

    <servlet-name>spring-dispatcher-servlet</servlet-name>

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

  </servlet-mapping>

<servlet-mapping>

    <servlet-name>spring-dispatcher-servlet</servlet-name>

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

  </servlet-mapping>

       DispatcherServlet本质上还是一个servlet,根据上面所介绍的servlet匹配顺序和规则很容易得出”/*”是路径匹配,只不过它是匹配所有路径,就是只有是页面请求的不管是客户端来的页面请求还是,服务器转发的页面请求都会被DispatcherServlet处理。

   现在对于把DispatcherServletservlet-mapping配置为”/*”出现的各种404错误就非常明显了。DispatcherServlet处理请求是我们自己实现的,但是所有请求(根据最长路径匹配规则,除非有更加精确的匹配,例如,/view/*)都到了DispatcherServlet显然我们没有实现的映射的部分就会出现404错误,例如各种静态资源的请求等。

       DispatcherServletservlet-mapping如果配置的是”/”,这个是个默认的servlet,就是在没有找到匹配的servlet的时候就使用DispatcherServlet。这个同样因为我们使用SpringMVC通常值配置了DispatcherServlet,而没有配置其他的servlet,对于这种情况”/””/*”相差不大。

   不过有一点DispatcherServletservlet-mapping如果配置的是”/”则可以访问的<welcome-file-list>设置的页面。而”/*”访问不到,这个原因还没有仔细研究。关于”/””/*”之间的差别在stackoverflow上有一个问题 ”/””/*” 的差别 对于这个问题的有的回答刚刚开始看觉得很对,但是通过不断的测试和更加深入的思考也会发现一些问题,可以参考测试一下,顺便想一想Servlet的匹配规则。

 

三、DispatcherServlet servlet-mapping配置策略参考

   配置url-pattern的时候尽量使用更加具体的模式,例如使用后缀匹配,例如,*.do,*.html,*.action等方式。

   如果你觉得上面的方式不够优雅,可以把要按模块设置前缀,例如,使用/user/*,/shop/*这样的方式来处理。

   对于静态资源的映射可以直接通过像下面的方式直接通过default Servlet来解决,这样就避免过多的转发,提高效率。

<servlet-mapping>

    <servlet-name>default</servlet-name>

    <url-pattern>/img/*</url-pattern>

  </servlet-mapping>

 

 

四、其他一些问题

   经常有一些404错误很难找到原因,因为很多情况可以引起服务器返回404错误,我没有读过tomcat的源码,所以很难知道根本原因,不过对于使用SpringMVC如果出现了像下面这样常见的错误。

No mapping found for HTTP request with URI [/context/xxx/xxx] in DispatcherServlet with name 'spring-dispatcher-servlet'

   那么就是servlet容器已经找到了DispatcherServlet但是DispatcherServlet没有设置相应的映射的方法。

   还有就是路径前面带不带”/”的的区别,这里可以使用一个小技巧帮助记忆,就是服务器端(servlet,web.xmlRequestMapping等配置中)的路径加上”/”表示的是相对于上下文环境(context),例如,

request.getRequestDispatcher("/index.html").forward(request, response);

   假如是本地服务器,端口是8080项目上下文环境是test,那么上面的就表示转发到http://localhost:8080/test/index.html这个路径。而在前端页面(jsp中编译也是这个原则)中的”/”则表示根路径,例如,在jsp中有链接是”/index.html”表示的就是http://localhost:8080/index.html这个路径。

   对于没有加上”/”的路径都是相对路径,就是当前文件所在目录的路径加上指定的链接。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!