当前位置: 代码迷 >> VC/MFC >> Spring MVC Restful构建中静态资源访问有关问题
  详细解决方案

Spring MVC Restful构建中静态资源访问有关问题

热度:469   发布时间:2016-05-02 03:57:01.0
Spring MVC Restful构建中静态资源访问问题

在构建Spring MVC Restful风格的应用时,由于在web.xml中:

<span style="font-family:Microsoft YaHei;font-size:18px;"><servlet>	<servlet-name>story</servlet-name>	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>	<load-on-startup>1</load-on-startup></servlet><servlet-mapping>	<servlet-name>story</servlet-name>	<url-pattern>/</url-pattern></servlet-mapping></span>

拦截了所有的请求,当然也包括对静态资源的请求拦截,如页面对image,css,js文件的引用,但是并没有定义相应的Controller来对这些请求进行响应,因此这些请求通常是无法完成的。

说到这里,我们应该想一个问题:Tomcat中,只有servlet能够处理请求,即使是jsp,也会被编译成 servlet。请注意,servlet容器中,由servlet处理这些资源那是一定了。不过,不同的 servlet 容器/应用服务器,处理这些静态资源的 servlet 的名字不大一样:
Tomcat, Jetty, JBoss, and GlassFish:默认 Servlet 名字为 "default";
Google App Engine:默认 Servlet 名字为 "_ah_default";
Resin:默认 Servlet 名字为 "resin-file";
WebLogic:默认 Servlet 名字为 "FileServlet";
WebSphere:默认 Servlet 名字为 "SimpleFileServlet";


解决以上问题常用的解决方案有以下几种:

方案一:激活 Tomcat 的 defaultServlet 来处理静态资源

<span style="font-family:Microsoft YaHei;font-size:18px;"><servlet-mapping>	<servlet-name>default</servlet-name>	<url-pattern>*.css</url-pattern></servlet-mapping><servlet-mapping>	<servlet-name>default</servlet-name>	<url-pattern>*.js</url-pattern></servlet-mapping><servlet-mapping>	<servlet-name>default</servlet-name>	<url-pattern>*.jpg</url-pattern></servlet-mapping></span>

每种类型的静态资源需要分别配置一个servlet-mapping,同时,要写在 DispatcherServlet 的前面, 让defaultServlet先拦截。(是不是一定要放在DispatcherServlet 前,需要您验证)
但是这样还会有个问题,就是无法访问到classpath下的资源文件,看了tomcat的DefaultServlet的配置项,似乎也没有可以指定目录的地方。


方案二:Spring 3.0.4 以后版本提供了<mvc:resources/> 

<!-- 处理静态资源 -->  

<span style="font-family:Microsoft YaHei;font-size:18px;"><!-- 上传的图片缓存1个月,其他js,css,img资源缓存一年 -->  <mvc:resources mapping="/res/**" location="/res/" cache-period="2592000"/><mvc:resources mapping="/css/**" location="/css/" cache-period="31536000"/><mvc:resources mapping="/js/**" location="/js/" cache-period="31536000"/><mvc:resources mapping="/img/**" location="/img/" cache-period="31536000"/></span>

mapping映射到 ResourceHttpRequestHandler 进行处理,location 指定静态资源的位置,可以是 web application根目录下、jar 包里面,这样可以把静态资源压缩到 jar包中。cache-period可以使得静态资源进行web cache。 

使用 <mvc:resources /> 元素,会把 mapping 的 URI 注册到 SimpleUrlHandlerMapping 的 urlMap 中,key 为 mapping 的 URI pattern 值,而 value 为 ResourceHttpRequestHandler,这样就巧妙的把对静态资源的访问由 HandlerMapping 转到 ResourceHttpRequestHandler 处理并返回,所以就支持 classpath 目录, jar 包内静态资源的访问。 


方案三:使用<mvc:default-servlet-handler/>
<mvc:default-servlet-handler/>会把 "/**"url注册到SimpleUrlHandlerMapping的urlMap中,把对静态资源的访问由 HandlerMapping 转到 org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler处理并返回。DefaultServletHttpRequestHandler 使用就是各个 Servlet 容器自己的默认 Servlet。 

补充说明下以上提到的HandlerMapping的order的默认值:
DefaultAnnotationHandlerMapping:0
<mvc:resources/> 自动注册的SimpleUrlHandlerMapping:2147483646
<mvc:default-servlet-handler/> 自动注册的 SimpleUrlHandlerMapping:2147483647

Spring 会先执行 order 值比较小的。当访问一个 a.jpg 图片文件时,先通过 DefaultAnnotationHandlerMapping 来找处理器,一定是找不到的,我们没有叫 a.jpg 的 Controller。再按 order 值升序找,由于最后一个 SimpleUrlHandlerMapping 是匹配 "/**" 的,所以一定会匹配上,再响应图片。 
Spring MVC 中,访问一个图片,还要走层层匹配。性能肯定好不到哪里去。不仅仅是 Spring MVC,即便 Struts,它们毕竟存活于servlet 容器,只要由servlet容器处理这些静态资源,必然要将这些资源读入JVM 的内存区中。所以,处理静态资源,我们通常会在前端加 apache 或 nginx。
另外,性能最好的应该是直接利用容器的DefaultServlet,让它最先拦截静态资源请求,这样就避免了后续的转发等操作,提高了性能,但是无法访问classpath下的资源文件。而通过mvc:resources标签可以简单配置匹配规则和资源文件路径,应该说是最简单快捷的一种方式,当然这大概也是mvc命名空间设计的初衷。


一般情况下,如果我们使用Spring MVC来开发我们的应用的话,常用方案二来解决问题,也常使用<c:url value=""/>来引用一些资源,但对于像在css中引用的资源由于没有contextPath还是无法请求到,如body{background-image:url(../image/bg.jpg)},这时可以结合方案一,在web.xml中加入:

<span style="font-family:Microsoft YaHei;font-size:18px;"><servlet-mapping>	<servlet-name>default</servlet-name>	<url-pattern>*.jpg</url-pattern></servlet-mapping></span>

备注:<c:url>标签作用是将一个URL地址格式化为一个字符串,并且保存在一个变量当中。它具有URL自动重写功能。value指定的URL可以是当前工程的一个URL地址,也可以是其他web工程的URL。但是这时需要context属性。也可以添加需要传递的参数。

属性:
var :变量名称;
value:要格式化的URL;
scope:作用域范围,默认为page;
context:其他工程路径;

<c:url var="urlStr" value="/user.jsp" ><c:param name="id" value="111" /></c:url><c:url var="urlStr" value="/user.jsp" context="/project" /><!--同一容器的其他web应用--><c:out value="${urlStr}" /><a href="${urlStr}"" >测试</a>

版权声明:本文为博主原创文章,未经博主允许不得转载。

  相关解决方案