作了一個demo,放到碼雲上了,有興趣的點我。html
一個良好的HTTP緩存策略能夠顯著地增進web應用的性能和其客戶端的體驗。主要使用」Cache-Control」 HTTP response header來完成,配合conditional headers例如」Last-Modified」和」ETag」。git
「Cache-Control」 HTTP response header 會建議私有緩存(如瀏覽器)和公開緩存(如代理)如何緩存HTTP response以供未來複用。web
「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
本部分描述了在一個Spring Web MVC 應用中配置HTTP caching的可行方式。瀏覽器
一、 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) :網絡
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();
二、支持靜態資源的HTTP caching supportmvc
靜態資源應該使用合適的 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>
三、在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 個變體:
當接收 conditional GET/HEAD requests時, checkNotModified 會檢查resource是否沒有被修改;若是沒有,它會返回一個HTTP 304 Not Modified response。
而在POST/PUT/DELETE requests時,checkNotModified 會檢查resouce是否沒有被修改;若是有修改,會返回一個HTTP 409 Precondition Failed response 來阻止併發修改。
四、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