1. 程式人生 > >96. Spring Boot之靜態資源版本對映(解決js/css快取問題)

96. Spring Boot之靜態資源版本對映(解決js/css快取問題)

我們在之前有一篇文章中講過【處理靜態資源】,但是在實際開發中,我們會發現我們所瞭解到的知識遠遠不夠我們使用,今天這節就是在實際開發當中對碰到的問題進行一定的講解和解決。

       問題的提出:我們對於我們編寫的js和css檔案,經常會做一些改變,由於瀏覽器快取,使用者本地的資源還是舊資源,一般為了解決這種情況導致的問題,我們會可能會選擇在資原始檔後面加上引數“版本號”或其他方式。

       使用版本號引數,如:

<scripttype="text/javascript"src="/js/common.js?v=1.0.1"></script>

       使用這種方式,當我們檔案修改後,手動修改版本號來達到URL檔案不被瀏覽器快取的目的。同樣也存在很多檔案都需要修改的問題,或者有的人會增加時間戳的形式,這樣是最不可取的,每次瀏覽器都需要為伺服器增加了不必要的壓力。Spring在解決這種問題方面,提供了2中解決方式。我們看看本節的大綱吧。

本章大綱 (1)回顧預設資源對映
(2)使用webjars
(3)Spring 靜態資源版本對映之資源名稱md5方式
(4)Spring 靜態資源版本對映之資源版本號方式
(5)md5與版本號方式的處理原理
(6)總結

       接下來我們看看具體是怎麼操作的。

(1)回顧預設資源對映

       預設配置的 /** 對映到 /static (或/public、/resources、/META-INF/resources)

其中預設配置的 /webjars/** 對映到 classpath:/META-INF/resources/webjars/

(2)使用webjars

       先說一下什麼是webjars?我們在Web開發中,前端頁面中用了越來越多的JS或CSS,如jQuery等等,平時我們是將這些Web資源拷貝到Java的目錄下,這種通過人工方式拷貝可能會產生版本誤差,拷貝版本錯誤,前端頁面就無法正確展示。

       WebJars 就是為了解決這種問題衍生的,將這些Web前端資源打包成Java的Jar包,然後藉助Maven這些依賴庫的管理,保證這些Web資源版本唯一性。

       WebJars 就是將js, css 等資原始檔放到 classpath:/META-INF/resources/webjars/ 中,然後打包成jar 釋出到maven倉庫中。

       以jQuery為例,檔案存放結構為:

       META-INF/resources/webjars/jquery/2.1.4/jquery.js

    META-INF/resources/webjars/jquery/2.1.4/jquery

.min.js

    META-INF/resources/webjars/jquery/2.1.4/jquery.min.map

META-INF/resources/webjars/jquery/2.1.4/webjars-requirejs.js

       使用方式就是在pom.xml檔案新增配置:

        <dependency>

           <groupId>org.webjars</groupId>

           <artifactId>jquery</artifactId>

           <version>2.1.4</version>

</dependency>

       Spring Boot 預設將 /webjars/** 對映到 classpath:/META-INF/resources/webjars/ ,結合我們上面講到的訪問資源的規則,便可以得知我們在頁面中引入jquery.js的方法為:

<script type="text/javascript" src="/webjars/jquery/2.1.4/jquery.js"></script>

       版本號統一管理

       但是我們實際開發中,可能會遇到升級版本號的情況,如果我們有100多個頁面,幾乎每個頁面上都有按上面引入jquery.js 那麼我們要把版本號更換為3.0.0,一個一個替換顯然不是最好的辦法。 使用webjarswebjars-locator就可以解決以上的問題,那麼具體要怎麼操作呢?

       首先在pom.xml新增webjars-locator的依賴:

       <dependency>

           <groupId>org.webjars</groupId>

           <artifactId>webjars-locator</artifactId>

       </dependency>

       增加一個WebJarsController,這個Controller會將以webjarslocator路徑攔截,然後重新組裝處理,具體程式碼如下:

package com.kfit;

import javax.servlet.http.HttpServletRequest;

import org.springframework.core.io.ClassPathResource;

import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.servlet.HandlerMapping;

import org.webjars.WebJarAssetLocator;

/**

 * 這個Controller會將以webjarslocator路徑攔截,然後重新組裝處理

 * @author Angel --守護天使

 * @version v.0.1

 * @date 2016121

 */

@Controller

public class WebJarsController {

    privatefinal WebJarAssetLocator assetLocator = new WebJarAssetLocator();

    @ResponseBody

    @RequestMapping("/webjarslocator/{webjar}/**")

    public ResponseEntity<Object> locateWebjarAsset(@PathVariable String webjar, HttpServletRequest request) {

        try {

            String mvcPrefix = "/webjarslocator/" + webjar + "/"// This prefix must match the mapping path!

            String mvcPath = (String)request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);

            String fullPath = assetLocator.getFullPath(webjar,mvcPath.substring(mvcPrefix.length()));

            return new ResponseEntity<>(new ClassPathResource(fullPath), HttpStatus.OK);

        } catch (Exception e) {

            return new ResponseEntity<>(HttpStatus.NOT_FOUND);

        }

    }

}

最後在頁面中使用的方式:

<script type="text/javascript" src="/webjarslocator/jquery/jquery.js"></script>

(3)Spring 靜態資源版本對映之資源名稱md5方式

       Spring 預設提供了靜態資源版本對映的支援。

       當我們的資源內容發生改變時,由於瀏覽器快取,使用者本地的資源還是舊資源,為了防止這種情況發生導致的問題。我們可能會選擇在資原始檔後面加上引數“版本號”或其他方式。

<scripttype="text/javascript"src="/js/demo.js?v=1.0.1"></script>

       使用這種方式,當我們檔案修改後,手工修改版本號來達到URL檔案不被瀏覽器快取的目的。同樣也存在很多檔案都需要修改的問題。或者有的人會增加時間戳的方式,這樣我認為是最不可取的,每次瀏覽器都要請求為伺服器增加了不必要的壓力。

       然而Spring在解決這種問題方面,提供了2種解決方式。 第一種方式就是MD5的方式,我們看下具體怎麼操作。

       第一步:修改 application.properties 配置檔案

spring.resources.chain.strategy.content.enabled=true

spring.resources.chain.strategy.content.paths=/**

       第二步:建立 ResourceUrlProviderController 檔案

package com.kfit;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.ControllerAdvice;

import org.springframework.web.bind.annotation.ModelAttribute;

import org.springframework.web.servlet.resource.ResourceUrlProvider;

/**

 *

 * @author Angel --守護天使

 * @version v.0.1

 * @date 2016121

 */

@ControllerAdvice

public class ResourceUrlProviderController {

    @Autowired

    private ResourceUrlProvider resourceUrlProvider;

    @ModelAttribute("urls")

    public ResourceUrlProvider urls() {

        return this.resourceUrlProvider;

    }

}

       第三步:在頁面中進行使用:

<scripttype="text/javascript"src="${urls.getForLookupPath('/js/demo.js') }"></script>

       如果使用的thymeleaf模板引擎的話,那麼需要這麼進行編寫:

<scripttype="text/javascript"th:src="${urls.getForLookupPath('/js/demo.js') }"></script>

注:這個博主經過測試,證明可用。

       當我們訪問頁面後,HTML中實際生成的程式碼為:

<scripttype="text/javascript"src="/js/demo--ef8d9e1da763788be348c78ea32a3c6d.js"></script>

(4)Spring 靜態資源版本對映之資源版本號方式

       資源版本號方式對所有資源的統一版本控制,不像上面一個md5是針對檔案的。

       除了在 application.properties中的配置有所區別,頁面使用和md5的一樣。

spring.resources.chain.strategy.fixed.enabled=true

spring.resources.chain.strategy.fixed.paths=/js/**,/v1.0.0/**

spring.resources.chain.strategy.fixed.version=v1.0.0

       這樣配置後,以上面 common.js 為例,實際頁面中生成的HTML程式碼為:

<scripttype="text/javascript"src="/v1.0.0/js/demo.js"></script>

(5)md5與版本號方式的處理原理

       頁面中首先會呼叫urls.getForLookupPath方法,返回一個/v1.0.0/js/demo.js或/css/demo-c6b7da8fffc9be141b48c073e39c7340.js然後瀏覽器發起請求。

       當請求的地址為md5方式時,會嘗試url中的檔名中是否包含-,如果包含會去掉後面這部分,然後去對映的目錄(如/static/)查詢/js/common.js檔案,如果能找到就返回。

       當請求的地址為版本號方式時,會在url中判斷是否存在/v1.0.0 ,如果存在,則先從URL中把 /v1.0.0 去掉,然後再去對映目錄查詢對應檔案,找到就返回。

(6)總結

       有這麼多方式來管理我們的資原始檔,然而在實際應用中雖然也都有可能用到(存在就有存在的道理嘛),但是憑藉個人經驗來說。

        <1>. 我們使用第三方的庫時,建議使用webjars的方式,通過動態版本號(webjars-locator 的方式)來使用(因為第三方庫在專案開發中變動頻率很小,即便是變動也是版本號的修改)。

       <2>. 我們使用自己存放在靜態資源對映目錄中的資源的時候,建議使用md5 資原始檔名的方式來使用(專案開發中一些css、js檔案會經常修改)。

       <3>. 專案素材檔案建議放到 classpath:/static (或其他)目錄中,打包在專案中,通過CMS維護的一些圖片和資源,我們使用配置引用到具體的磁碟絕對路徑來使用。

       <4>. 注意使用md5檔名方式的時候,Spring 是有快取機制的,也就是說,在服務不重啟的情況下,你去變動修改這些資原始檔,其檔名的md5值並不會改變,只有重啟服務再次訪問才會生效。如果需要每次都獲取實際檔案的md5值,需要重寫相關類來實現,我們不建議這樣做,因為一直去計算檔案md5值是需要效能代價的。