1. 程式人生 > >Spring mvc HTTP協議之快取機制

Spring mvc HTTP協議之快取機制

概述

Spring MVC 支援HTTP協議的 Last-Modified 快取機制。

  1. 在客戶端地一次輸入URL時,伺服器端會返回內容和狀態碼200, 表示請求成功,同時會新增一個“Last-Modified”屬性,表示該請求資源的最後修改時間
  2. 客戶端第二次請求此URL時,客戶端會向伺服器傳送請求頭 “IF-Modified-Since”,如果服務端內容沒有變化,則自動返回HTTP304狀態碼(只返回相應頭資訊,不返回資原始檔內容,這樣就可以節省網路頻寬,提供響應速度和使用者體驗)

Spring MVC 中實現示例

UserCacheController.java

@Controller
public
class UserCacheController extends AbstractController implements LastModified{ private long lastModified = System.currentTimeMillis(); protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { List<User> userList = new
ArrayList<User>(); userList.add(new User("zhangsan", 18)); userList.add(new User("wangwu", 16)); System.out.println("執行一次,我有快取"); return new ModelAndView("userList", "users", userList); } @Override public long getLastModified(HttpServletRequest request)
{ //時間戳邏輯,返回最後修改時間,例如 if (lastModified == 0L) { lastModified = System.currentTimeMillis(); } System.out.println("時間戳:"+lastModified); return lastModified; } }

Spring MVC 提供的Last-Modified機制的支援,只需要實現LastModified介面,並實現GetLastModified() 方法,每次修改資源的時候,更新下lastModified的值即可。

userList.jsp

<%@page import="java.util.List"%>
<%@page import="cn.com.infcn.bean.User"%>
<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
    <%
        List<User> userList = (List<User>) request.getAttribute("users");
        for (User user : userList) {
    %>
    使用者名稱:<%=user.getUserName()%><br /> 年齡:<%=user.getAge()%><br />
    <hr>
    <%
        }
    %>
</body>
</html>

訪問效果



只有第一次執行了Controller,以後訪問都沒執行Controller。

原理分析

DispatcherServlet.doDispatch()


  1. 首先獲取 http 請求的method type。
  2. 如果method 是 “GET”或“HEAD” 才支援快取機制
  3. 通過 HandlerAdapter.getLastModified() 方法獲取 UserCacheController 中的lastModified 的值,最後修改時間。
  4. 呼叫 checkNotModified() 方法驗證 http 請求頭中的“If-Modified-Since”的時間進行對比,判斷頁面是否更新過。如果有更新才執行具體的Controller, 沒有更新則響應 304 狀態碼資訊(HTTP 304: Not Modified )。

getLastModified()



通過handler的介面卡類,然後在呼叫UserCacheController.getLastModified() 方法獲取最後更新時間。

checkNotModified()

Paste_Image.png
Paste_Image.png
  1. 呼叫 validateIfModifiedSince() 方法獲取http請求頭中的“If-Modified-Since”值,並驗證是否修改過。
    沒有修改過則設定notModified=true,如果修改過則設定notModified=false。
  2. 如果 notModified=true,則設定response響應狀態碼304或412
  3. 如果是GET 或 HEAD 請求則新增響應頭“Last-Modified”

validateIfModifiedSince()


  1. 解析http 請求頭中的“If-Modified-Since”值
  2. 判斷快取頁面是否需要更新。
    注:(lastModifiedTimestamp / 1000 * 1000):因為http頭中只儲存到秒,所以這裡把秒後面的置為0。

HTTP 請求響應頭分析



通過瀏覽器F12 可以看出:

  1. 每次請求都會攜帶“If-Modified-Since”資訊到伺服器驗證資源是否需要更新。
  2. 伺服器響應頭中會包含“Last-Modified”資訊,訪問資源最後修改的日期。

快取限制條件

並不是所有MappingHandler 方式都支援快取。
比如:DefaultAnnotationHandlerMapping 就不支援快取機制。
因為支援註解的Controller中可以有多個請求方法,而每個方法都需要計算檔案的最後修改時間,這樣LastModified就不適用了。只適用一個Controller中只支援一個請求的HandlerMapping。

AnnotationMethodHandlerAdapter


從程式碼中可以看出,這個方法永遠返回-1。