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>

發佈了144 篇原創文章 · 獲贊 29 · 訪問量 35萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章