Spring 4 官方文檔學習(十一)Web MVC 框架之HTTP caching support


做了一個demo,放到碼雲上了,有興趣的點我


 

一個良好的HTTP緩存策略可以顯著地增進web應用的性能和其客戶端的體驗。主要使用”Cache-Control” HTTP response header來完成,配合conditional headers例如”Last-Modified”和”ETag”。

“Cache-Control” HTTP response header 會建議私有緩存(如瀏覽器)和公開緩存(如代理)如何緩存HTTP response以供將來復用。

 

“ETag” (entity tag) 是由兼容HTTP/1.1 的web server返回的HTTP response header,用於判斷給定URL的內容的改變。它可被認為是”Last-Modified” header的更復雜的繼承者。 當服務器返回了一個帶有ETag header的representation時,客戶端可以在后續的GETs中使用該header -- 在一個”If-None_Match” header中。 如果內容沒有改變,server會返回 “304: Not Modified”。

 

本部分描述了在一個Spring Web MVC 應用中配置HTTP caching的可行方式。

 

1、 Cache-Control HTTP header

Spring Web MVC支持很多使用環境和方式來配置一個應用的Cache-Control headers。RFC 7234 Section 5.2.2詳細的描述了該header以及其可能的directives,有幾種不同的方式來實現常用的案例。

 

Spring Web MVC 在其幾個APIs中使用了一個配置慣例:setCachePeriod(int seconds) :

  • -1 代表不會生成Cache-Control response header。
  • 0 代表會阻止緩存,使用Cache-Control: no-store directive。
  • n>0 會緩存給定的response,持續n秒,使用 Cache-Control: max-age=n directive。

 

CacheControl 構造類簡單的描述了可用的 Cache-Control directives,可以更簡單的build你自己的HTTP caching 策略。 一旦構造完畢,一個CacheControl實例可以在幾個Spring Web MVC APIs中被用作參數。

// Cache for an hour - "Cache-Control: max-age=3600"
CacheControl ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS);

// Prevent caching - "Cache-Control: no-store"
CacheControl ccNoStore = CacheControl.noStore();

// Cache for ten days in public and private caches,
// public caches should not transform the response
// "Cache-Control: max-age=864000, public, no-transform"
CacheControl ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS)
                                    .noTransform().cachePublic();

 

2、支持靜態資源的HTTP caching support

靜態資源應該使用合適的 Cache-Control以及conditional headers來優化性能。 配置一個ResourceHttpRequestHandler來服務靜態資源,不僅會天然地寫入 Last-Modified headers (通過讀取文件的metadata),還會寫入 Cache-Control headers -- 如果正確的配置了。

 

你可以設置ResourceHttpRequestHandler的cachePeriod attribute 或者使用一個CacheControl實例,它們都可以支持更多特定的directives:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
                .addResourceLocations("/public-resources/")
                .setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic());
    }

}

在XML中:

<mvc:resources mapping="/resources/**" location="/public-resources/">
    <mvc:cache-control max-age="3600" cache-public="true"/>
</mvc:resources>

 

3、在Controllers中支持 Cache-Control、ETag、Last-Modified response headers

Controllers可以支持 Cache-Control、ETag、和/或 If-Modified-Since HTTP request;如果response設置了一個Cache-Control header,非常推薦這樣做。這會計算給定request的一個lastModified long 和/或 一個Etag value,將其與 If-Modified-Since request header value作比較,並可能返回一個status code 304 (Not Modified) 的response。

 

如同在“Using HttpEntity”部分描述的,controllers可以使用HttpEntity 類型來與request/response 交互。

返回ResponseEntity的Controllers 可以在responses中以如下方式包含HTTP caching information:

@GetMapping("/book/{id}")
public ResponseEntity<Book> showBook(@PathVariable Long id) {

    Book book = findBook(id);
    String version = book.getVersion();

    return ResponseEntity
                .ok()
                .cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS))
                .eTag(version) // lastModified is also available
                .body(book);
}

這樣做,不僅會在response中帶有ETag和Cache-Control headers,還會 將response轉換成 一個響應體為空的HTTP 304 Not Modified response -- 如果客戶端發送的conditional headers 匹配Controller設置的caching information。

 

一個@RequestMapping method 可能也會希望支持同樣的行為。 可以這樣做:

@RequestMapping
public String myHandleMethod(WebRequest webRequest, Model model) {

    long lastModified = // 1. application-specific calculation

    if (request.checkNotModified(lastModified)) {
        // 2. shortcut exit - no further processing necessary
        return null;
    }

    // 3. or otherwise further request processing, actually preparing content
    model.addAttribute(...);
    return "myViewName";
}

這里有兩個關鍵元素: 調用 request.checkNotModified(lastModified)、返回null。前者在其返回true之前設置了合適的response status和headers。后者,結合前者,會讓Spring MVC 不再更進一步地處理該request。

 

注意,這里有3 個變體:

  • request.checkNotModified(lastModified) 會比較lastModified 和 If-Modified-Since 或 If-Unmodified-Since request header。
  • request.checkNotModified(eTag) 會比較 eTag 和 If-None-Match request header。
  • request.checkNotModified(eTag, lastModified) 二者都比較,意味着兩種條件應該都有效。

 

當接收 conditional GET/HEAD requests時, checkNotModified 會檢查resource是否沒有被修改;如果沒有,它會返回一個HTTP 304 Not Modified response。

而在POST/PUT/DELETE requests時,checkNotModified 會檢查resouce是否沒有被修改;如果有修改,會返回一個HTTP 409 Precondition Failed response 來阻止並發修改。

 

4、Shallow ETag support

對於ETags的支持是由Servlet filter ShallowEtagHeaderFilter提供的。 這是一個簡單的Servlet Filter,因此可與任何web框架結合使用。 ShallowEtagHeaderFilter filter 會創建 shallow ETags (與deep ETags相對,后面有講)。 該filter會緩存被渲染的JSP的內容(或其他內容),生成一個MD5 hash,並將其返回作為response的一個ETag header。等下次客戶端請求同樣的資源時,它會使用該hash作為 If-None-Match value。 該filter會偵測到它,重新渲染視圖,然后比較二者的hash。 如果相等,返回304。

 

注意,這種策略節省了網絡帶寬,而非CPU,因為仍然需要為每次request計算response。而controller級別的其他策略(上面講過的),則既能節省帶寬,又能避免計算。

 

該filter有一個writeWeakETag parameter,是用來配置該filter寫入Weak ETags的,就像這樣:W/"02a2d595e6ed9a0b24f027f2b63b134d6", 如同 RFC 7232 Section 2.3 中定義的一樣。

 

你可以在web.xml中配置 ShallowEtagHeaderFilter:

<filter>
    <filter-name>etagFilter</filter-name>
    <filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</filter-class>
    <!-- Optional parameter that configures the filter to write weak ETags
    <init-param>
        <param-name>writeWeakETag</param-name>
        <param-value>true</param-value>
    </init-param>
    -->
</filter>

<filter-mapping>
    <filter-name>etagFilter</filter-name>
    <servlet-name>petclinic</servlet-name>
</filter-mapping>

或者在Servlet 3.0+ 環境中:

public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {

    // ...

    @Override
    protected Filter[] getServletFilters() {
        return new Filter[] { new ShallowEtagHeaderFilter() };
    }

}

 

See Section 22.15, “Code-based Servlet container initialization” for more details.

 

 

 

 

官方文檔鏈接:

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-caching


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM