我们在一般在配置 SSM 项目时会将 DispatcherServleturl-pattern 配置为 *.do*.action/*/, 今天就来分析一下它们的区别。以及 Tomcat 中内置的两个 Servlet: DefaultServletJspServlet

url-pattern

我们来列举一下在 web.xml 中 url-pattern 的几种情况:

匹配规则 url-pattern 举例
精确匹配 /login /login, 不能匹配 /login/
前缀匹配 /user/* /user、/user/、/user/home、user/index
后缀匹配 *.do /getUser.do、/addUser.do
缺省匹配 / 所有没有被映射的路径都将由该匹配规则处理, 如 default servlet
  • 可以将 /* 认为是一种特殊的前缀匹配。
  • * 只有位于 url-pattern 的开头或结尾时才能作为通配符使用。
  • 优先级从上到下依次降低, 即: 越精准的匹配优先级越高。其中 /* 的优先级介于前缀匹配和后缀匹配之间。

DefaultServlet 和 JspServlet

我们先来看一下 default servlet 是什么。

tomcat/web.xml at 9.0.x · apache/tomcat - GitHub

这是 Tomcat 中的 basic web.xml, 它将对容器内的所有 webapp 生效 (除非里面的内容被覆盖)。

通过 basic web.xml 我们可以看出, 在 Tomcat 中内置了两个 Servlet: DefaultServlet 和 JspServlet, 且它们的 url-pattern 如下

<!-- The mapping for the default servlet -->
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<!-- The mappings for the JSP servlet -->
<servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.jspx</url-pattern>
</servlet-mapping>

也就是说我们在 Tomcat 中的所有 webapp 都内置了两个 servlet, 其中 JspServlet 用于处理所有以 *.jsp 为后缀的请求, 而 DefaultServlet 用于处理那些没有被映射的请求, 比如: cssjsjpg ...

你可以在 tomcat/DefaultServlet.java at main · apache/tomcat - GitHub 阅读 DefaultServlet 的源代码。

同理, 你应该也可以在 GitHub 上找到 JspServlet 的源代码, 这里不再赘述。

通过上面的分析, 可以了解到 Tomcat 是如何加载 jsp 文件和静态资源的, 接下来看一下我们该如何配置 DispatcherServleturl-pattern

在 SpringMVC 的配置文件中有一个 <mvc:default-servlet-handler/> 标签, 它的作用就是向 IOC 容器中注册一个 org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler 的对象用于处理那些 SpringMVC 未映射的请求 (如: css、js、jpg 等静态资源或其他错误的访问路径), 其本质还是将这些请求交给了 Tomcat 中内置的 DefaultServlet 来处理。

因此我个人认为 DispatcherServlet 映射路径的最佳实践如下:

<servlet>
  <servlet-name>dispatcherServlet</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-webmvc.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>dispatcherServlet</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

根据以上的分析可知: 在 web.xml 中这样配置 DispatcherServlet 的 url-pattern 后 DefaultServlet 将不会生效 (因为 DefaultServlet 的 url-pattern 被覆盖了), 但是 JspServlet 将不受影响, 因此只需要在 SpringMVC 的配置文件中添加 <mvc:default-servlet-handler/> 标签再将静态资源的请求交给 DefaultServlet 处理即可。

但是如果把 DispatcherServlet 的 url-pattern 配置为 /* 那么根据 url-pattern 的优先级, JspServlet 也将不会生效, 因此此时再去访问 jsp 页面将出现 404 错误 (因为 DefaultServlet 也无法处理 jsp 页面的请求, 它只可以处理静态资源的请求)。