这篇文章,主要介绍SpringMVC处理静态资源的几种方式。
目录
一、SpringMVC静态资源
1.1、静态资源404问题
(1)问题引入
SpringMVC框架中,前端控制器DispatcherServlet是拦截【/】请求,这个【/】就是会拦截所有的前端请求,但是对于jsp资源例外。为什么这么说呢???为什么SpringMVC不会拦截JSP文件呢???
下面通过一个案例来看下,SpringMVC对于静态资源html和jsp文件的访问效果。
- 创建一个工程,引入SpringMVC相关依赖,然后在【webapp】目录下面,新建两个静态资源文件:【demo.html】和【demo.jsp】。

- 启动工程,依次访问两个资源文件,例如:http://localhost:8080/springmvc/demo.jsp、http ://localhost:8080/springmvc/demo.html。

从上面的案例可以看出,SpringMVC框架中前端控制器DispatcherServlet会拦截所有的请求,但是不会拦截JSP资源文件,至于为什么呢???
(2)为什么不拦截JSP文件???
为了验证SpringMVC框架会不会处理JSP文件,那我们可以通过源码来看下,在DispatcherServlet类里面,给【doDispatch()】方法打一个断点,然后分别访问两个静态资源文件,看两次请求是否都会被断点拦截到。

- 首先访问html文件。

从断点可以看到,SpringMVC框架会拦截html静态资源。
- 接着访问jsp资源。
访问JSP之后,可以发现不会经过源代码中的断点,这就可以证明,SpringMVC框架中【/】拦截的请求不包含JSP资源。
具体原因是为什么呢????
要解释这个问题,首先需要知道【/】表示的什么意思???
- SpringMVC框架中【/】表示的是,如果当前请求没有找到处理的Servlet的话,那么这个时候就会将当前请求交给【/】斜杠对应的Servlet进行处理。
- 但是,对于JSP来说,它本质上也是Servlet,在之前学习JSP与Servlet的时候,可以发现,Tomcat里面会有两个默认的Servlet。
- 一个Servlet是DefaultServlet,它是用于处理那些找不到对应Servlet的请求的。
- 另一个是JspServlet,这个是专门用于处理JSP资源文件的Servlet。
- 从这里也可以看出来,SpringMVC框架本质上是通过DispatcherServlet替换了Tomcat里面对应的【DefaultServlet】,所以对于JSP文件来说,还是使用的Tomcat里面的JspServlet进行处理。
下面看下Tomcat里面web.xml文件中的两个Servlet。
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- The mapping for the default servlet -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<!-- 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>
从web.xml配置文件里面来看,JspServlet是处理【jsp】或者【jspx】为后缀的请求,而DefaultServlet是拦截的【/】,也就是处理那些找不到对应处理Servlet的请求。
这里我再解释一下,什么叫做DefaultServlet处理的是那些找不到对应处理Servlet的请求。
举个栗子:
- 假设现在有一个AServlet,它处理的【/aServlet】的请求。
- 有一个BServlet,它处理的【/bServlet】的请求。
- 现在,我们打开浏览器依次访问两个请求。
- 访问【/aServlet】,此时这个请求将会被【AServlet】进行处理;
- 访问【/bServlet】的时候,此时这个请求将会被【/bServlet】进行处理;
- 但是,现在如果我们访问【/cServlet】,由于我们没有编写专门用于处理【/cServlet】请求的【Servlet】,所以这个时候,Tomcat会将【/cServlet】交给【DefaultServlet】进行处理。
1.2、几种静态资源处理方式
由于SpringMVC的前端控制器DispatcherServlet只会根据请求去寻找对应的Handler,如果找不到就会抛出404,那要如何处理这种静态资源的情况呢???这里说的静态资源包括:html、js、css、img等等。
(1)开启默认Servlet
这种处理方式是在【springmvc】的配置文件里面,添加如下配置:
<!-- 开启默认servlet -->
<mvc:default-servlet-handler/>
当我们添加了这个配置之后,我们再次访问前面的demo.html文件,此时可以正常访问。

<mvc:default-servlet-handler/>解决静态资源访问问题的原理:
当我们添加<mvc:default-servlet-handler/>这个配置之后,SpringMVC会定义一个DefaultServletHttpRequestHandler类,接着SpringMVC通过DefaultServletHttpRequestHandler类将当前请求转发到Tomcat容器里面的DefaultServlet,这个时候就相当于是Tomcat处理静态资源了,完美解决静态资源访问问题。

单纯的使用<mvc:default-servlet-handler/>配置,这个时候虽然可以解决静态资源的访问问题,但是同时,也出现了新的问题:就是SpringMVC中的所有请求都将被转发到Tomcat的DefaultServlet进行处理。
下面看下,如果我们项目里面有一个控制器Controller,有个demo方法,如下所示:
@RestController
@RequestMapping("/api")
public class IndexController {
@RequestMapping("/demo")
public String demo() {
return "IndexController Api Demo.";
}
}
通过访问上面的请求,可以发现出现404了。

如何解决404问题???
在【springmvc】的配置文件里面,添加【<mvc:annotation-driven/>】注解配置,如下所示:
<!-- 注解驱动 -->
<mvc:annotation-driven/>
也就是说,通过这种方式处理静态资源,需要同时配置两个内容,分别如下所示:
<!-- 开启默认servlet -->
<mvc:default-servlet-handler/>
<!-- 注解驱动 -->
<mvc:annotation-driven/>
再次访问静态资源和非静态资源,如下所示:

以上,就是通过开启默认Servlet以及注解驱动,解决SpringMVC框架的静态资源问题。
(2)mvc:resources
这种方式,是通过添加【<mvc:resources>】配置来解决静态资源处理问题,【<mvc:resources>】标签的属性如下:
【<mvc:resources>】标签属性:
- location:静态资源所在路径。
- mapping:这是拦截的请求路径,可以采用通配符。
举个栗子:
- 项目里面有三个静态资源目录,分别是:【pages】、【css】、【js】。
- 现在需要访问三个目录中的静态资源,此时,就可以通过【<mvc:resources>】标签进行配置。
<!-- 第二种方式: 处理静态资源 -->
<mvc:resources location="/pages/" mapping="/pages/**"/>
<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/js/" mapping="/js/**"/>
具体工程目录结构如下所示:

启动工程,打开浏览器,依次访问三个目录下的静态资源文件。

注意:如果出现404情况,可以看下打包之后的target目录下面,是否有静态资源的目录,没有的话重新打包一下。

但是,上面配置了三个静态资源目录,有点麻烦了,实际开发里面,都是将静态资源统一放到一个目录下面,然后只配置一个【<mvc:resources>】标签即可。实际开发中,都是将静态资源目录放在【static】目录下面。

【<mvc:resources>】标签是如何解决静态资源的问题呢???
SpringMVC框架中,如果是通过【<mvc:resources>】标签来解决的静态资源问题,那么SpringMVC框架会创建自己的静态资源处理对象,然后自己根据请求,去【<mvc:resources>】指定的目录下面进行查找资源,然后响应给客户端。
通过【<mvc:resources>】标签这种方式,可以完全不用关心Tomcat容器的DefaultServlet,也就是说和容器没有关系。
这种处理静态资源的方式,同样会存在非静态资源访问404的情况,也是需要通过【<mvc:annotation-driven>】。
(3)不拦截【/】斜杠请求
第三种处理静态资源的方式,既然SpringMVC框架出现静态资源访问问题,是因为拦截的【/】斜杠请求,那我们可以不拦截【/】,而是拦截我们自己指定的请求,例如:SpringMVC通过前端控制器DispatcherServlet只处理拦截【*.do】结尾的请求,这样当我们访问静态资源的时候,依然可以通过Tomcat的DefaultServlet进行处理,从而解决了静态资源问题。
<!-- 配置 DispatcherServlet 前端控制器 -->
<servlet>
<!-- servlet 名称 -->
<servlet-name>springMVC</servlet-name>
<!-- servlet处理类 -->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 设置 SpringMVC 配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<!-- 设置启动时候加载 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- servlet映射路径 -->
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<!-- 拦截【*.do】请求 -->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
以上,介绍了三种SpringMVC静态资源的处理方式,其实还有其他的几种处理方式,但是不太常用,所以这里不介绍了。
综上,这篇文章结束了,主要介绍SpringMVC处理静态资源的几种方式。
【源代码地址:https://gitee.com/zhuyoubin/ssm_code/tree/master/spring-mvc/springmvc-demo08】