1. 程式人生 > >菜鳥之路——Spring MVC(八)靜態資源

菜鳥之路——Spring MVC(八)靜態資源

  一、前言

   在SpringMVC中常用的就是Controller與View。但是我們常常會需要訪問靜態資源,如html,js,css,image等。如果將DispatcherServlet請求對映配置為”/”,則Spring MVC將捕獲Web容器所有的請求,包括靜態資源的請求,Spring MVC會將它們當成一個普通請求處理,因此找不到對應處理器將導致錯誤。如配置檔案如下:

<!-- Processes application requests -->
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
        
    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

  部署專案後程序載入或用瀏覽器訪問時出現類似的警告, WARN [org.springframework.web.servlet.PageNotFound] -<No mapping found for HTTP request with URI [/sandDemo001/images/1.jpg] in DispatcherServlet with name 'spring'>。
  原因在於:<servlet-mapping>的<url-pattern>/</url-pattern>把所有的請求都交給spring去處理了,而所有available的請求url都是在Constroller裡使用類似@RequestMapping(value = "/login/{user}", method = RequestMethod.GET)這樣的註解配置的,這樣的話對js/css/jpg/gif等靜態資源的訪問就會得不到。

二、訪問靜態資源的解決方法

  前兩種方法首先要在宣告 beans的xmlns裡面加入 xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd (根據版本決定)

採用mvc:default-servlet-handler

在springMVC-servlet.xml中配置mvc:default-servlet-handler 後,會在Spring MVC上下文中定義一個org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它會像一個檢查員,對進入DispatcherServlet的URL進行篩查,如果發現是靜態資源的請求,就將該請求轉由Web應用伺服器預設的Servlet處理,如果不是靜態資源的請求,才由DispatcherServlet繼續處理。

  一般Web應用伺服器預設的Servlet名稱是”default”,因此DefaultServletHttpRequestHandler可以找到它。如果你所有的Web應用伺服器的預設Servlet名稱不是”default”,則需要通過default-servlet-name屬性顯示指定:

<mvc:default-servlet-handler default-servlet-name="所使用的Web伺服器預設使用的Servlet名稱" /> 
  要注意的是:配置檔案中必須有新增:
<mvc:annotation-driven/>

  否則發現訪問@RequestMapping("/path1/path2")不能訪問,報錯WARNING: No mapping found for HTTP request with URI [/mvc/user/findUser/lisi/770] in DispatcherServlet with name 'springMVC'。原因在於:當兩種標籤都沒有的時候,框架預設註冊的有AnnotationMethodHandlerAdapter這個bean,能夠處理@RequestMapping這個註解,但是隻配置了<mvc:default-servlet-handler/>時,會缺少AnnotationMethodHandlerAdapter這個bean,進而無法進行訪問路徑的對映。當兩種標籤都有的時候,<mvc:annotation-driven/>會註冊一個RequestMappingHandlerAdapter的bean,這個bean能夠處理@RequestMapping這個註解。

  採用mvc:resources

  mvc:default-servlet-handler 將靜態資源的處理經由Spring MVC框架交回Web應用伺服器處理。而mvc:resources 更進一步,由Spring MVC框架自己處理靜態資源,並新增一些有用的附加值功能。  首先,mvc:resources 允許靜態資源放在任何地方,如WEB-INF目錄下、類路徑下等,你甚至可以將JavaScript等靜態檔案打到JAR包中。通過location屬性指定靜態資源的位置,由於location屬性是Resources型別,因此可以使用諸如”classpath:”等的資源字首指定資源位置。傳統Web容器的靜態資源只能放在Web容器的根路徑下mvc:resources 完全打破了這個限制。  其次,mvc:resources 依據當前著名的Page Speed、YSlow等瀏覽器優化原則對靜態資源提供優化。你可以通過cacheSeconds屬性指定靜態資源在瀏覽器端的快取時間,一般可將該時間設定為一年,以充分利用瀏覽器端的快取。在輸出靜態資源時,會根據配置設定好響應報文頭的Expires 和 Cache-Control值。  在接收到靜態資源的獲取請求時,會檢查請求頭的Last-Modified值,如果靜態資源沒有發生變化,則直接返回303相應狀態碼,提示客戶端使用瀏覽器快取的資料,而非將靜態資源的內容輸出到客戶端,以充分節省頻寬,提高程式效能。

  需要注意的是,使用<mvc:resources/>元素,把mapping的URI註冊到SimpleUrlHandlerMapping的urlMap中,key為mapping的URI pattern值,而value為ResourceHttpRequestHandler,這樣就巧妙的把對靜態資源的訪問由HandlerMapping轉到ResourceHttpRequestHandler處理並返回,所以就支援classpath目錄,jar包內靜態資源的訪問。另外需要注意的一點是,不要對SimpleUrlHandlerMapping設定defaultHandler.因為對static uri的defaultHandler就是ResourceHttpRequestHandler,否則無法處理static resources request.

  配置時,可在springMVC-servlet中新增如下配置:

<mvc:resources location="/,classpath:/META-INF/publicResources/" mapping="/resources/**"/>  

  以上配置將Web根路徑”/”及類路徑下 /META-INF/publicResources/ 的目錄對映為/resources路徑。假設Web根路徑下擁有images、js這兩個資源目錄,在images下面有bg.gif圖片,在js下面有test.js檔案,則可以通過 /resources/images/bg.gif 和 /resources/js/test.js 訪問這二個靜態資源。  假設WebRoot還擁有images/bg1.gif 及 js/test1.js,則也可以在網頁中通過 /resources/images/bg1.gif 及 /resources/js/test1.js 進行引用。

  補充說明:多個HandlerMapping的執行順序問題:
  DefaultAnnotationHandlerMapping的order屬性值是:0
  <mvc:resources/ >自動註冊的 SimpleUrlHandlerMapping的order屬性值是: 2147483646
  <mvc:default-servlet-handler/>自動註冊的SimpleUrlHandlerMapping 的order屬性值是: 2147483647
  spring會先執行order值比較小的。當訪問一個a.jpg圖片檔案時,先通過 DefaultAnnotationHandlerMapping 來找處理器,一定是找不到的,因為我們沒有叫a.jpg的Action。然後再按order值升序找,由於最後一個 SimpleUrlHandlerMapping 是匹配 "/**"的,所以一定會匹配上,就可以響應圖片。

  使用前兩種方法在訪問靜態資源時,如果有匹配的全域性攔截器,就會走攔截器。如果在攔截中實現許可權檢查,要注意過濾這些對靜態檔案的請求。最好把配置寫在攔截器前面。

用tomcat自帶的defaultServlet

  比如,在web.xml裡新增如下的配置:

<servlet-mapping>
     <servlet-name>default</servlet-name>
     <url-pattern>*.css</url-pattern>
</servlet-mapping>
 
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.gif</url-pattern>
 </servlet-mapping>
    
 <servlet-mapping>
     <servlet-name>default</servlet-name>
     <url-pattern>*.jpg</url-pattern>
 </servlet-mapping>
    
 <servlet-mapping>
     <servlet-name>default</servlet-name>
     <url-pattern>*.js</url-pattern>
 </servlet-mapping>
  要寫在DispatcherServlet的前面,讓 defaultServlet先攔截請求,這樣請求就不會進入Spring了。  

  主要還是推薦前兩種方法。