SpringMVC 处理静态资源请求

1. DispatcherServlet拦截路径导致静态资源无法访问的问题

首先谈一下Tomcat几个内置的用于处理请求资源的Servlet。

  • DefaultServlet:拦截所有的静态资源请求,返回给客户端
  • JspServlet:拦截所有的jsp请求,渲染jsp页面返回给客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!--
DefaultServlet会应用于所有的tomcat项目,用于处理静态资源请求。
哪些资源是静态资源呢?简单地讲,除了jsp/servlet(jsp本质还是servlet),其它的资源都是静态资源

DefaultServlet是如何处理静态资源的请求呢?以用户访问index.html为例
DefaultServlet会在服务器下找到index.html这个资源,并返回给用户
-->
<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>

<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<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>

<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>

DispatcherServlet常用URL映射拦截规则如下:

  • /*:拦截所有请求,优先级高于DefaultServlet和JspServlet的url-pattern,所以静态资源和jsp都无法获取
  • /:覆盖DefaultServlet的url-pattern,所以静态资源无法获取,jsp页面正常显示
  • .action.do:只拦截指定后缀的请求。静态资源和jsp都正常获取

DispatcherServlet的常用URL映射拦截规则如下,/*的拦截范围比/更大,还会拦截到*.jsp请求,这样jsp页面就无法显示了

2. 问题解决

你可能会想,那直接把DispatcherServleturl-pattern设置为.do之类的后缀,不能自然而然解决了吗。

但是实际上不建议处理带后缀的请求,因为优秀的REST风格的URL不希望带有任何后缀。

一般我们会把DispatcherServleturl-pattern设置为/,但是会导致静态资源无法获取。解决方法如下:

2.1. 方式一:激活Tomcat的defaultServlet来处理静态文件

给DefaultServlet添加多个url-pattern,能够处理各种静态文件

1
2
3
4
5
6
7
8
9
10
11
12
<servlet-mapping>      
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>

2.2. 方式二:使用<mvc:resources/>

<mvc:resources/>最灵活,可以拦截各种形式的请求。location可以是webapp的路径,也可以是classpath路径

1
2
3
4
5
6
7
<!-- resources mapping -->
<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/js/" mapping="/js/**"/>
<mvc:resources location="/images/" mapping="/images/**"/>
<mvc:resources location="/html/" mapping="/html/**"/>
<mvc:resources location="/fonts/" mapping="/fonts/**"/>
<mvc:resources location="/plugins/" mapping="/plugins/**"/>

2.3. 方式三:使用<mvc:default-servlet-handler/>

<mvc:default-servlet-handler/>的作用是,对进入DispatcherServlet的请求进行筛选

  • 将无法处理的请求(没有经过映射的请求)转发给DefaultServlet去处理
  • 如果是经过映射的请求,则交给DispatcherServlet继续进行处理

在springmvc.xml中配置如下:

1
<mvc:default-servlet-handler/>

配置<mvc:default-servlet-handler/>之后,你会发现所有的静态资源都可以访问了,但是访问@Controller中的url映射,都是返回404。这是因为default-servlet-handler只认识通过BeanNameUrlHandlerMapping方式配置的处理器,但是不认识@RequestMapping注解方式配置的处理器。所以default-servlet-handler误以为该请求没有对应的处理器,所以直接将给DispatcherServlet去处理,因为没有对应的资源,所以返回404

解决方法是加上<mvc:annotation-driven/>,这样就有HandlerMapping和HandlerAdapter支持注解方式的配置

1
2
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
panchaoxin wechat
关注我的公众号
支持一下