天天看點

SpringMVC源碼解析之Last-Modified緩存機制GetLastModified() 方法通路效果原理分析DispatcherServlet.doDispatch()并非MappingHandler都支援緩存

Spring MVC 支援HTTP協定的 Last-Modified 緩存機制。

支援上次修改的HTTP請求,以友善内容緩存。 相同的合同作為Servlet API中的getLastModified方法。

通過委派到org.springframework.web.servlet.HandlerAdapter.getLastModified實施。 預設情況下,任何控制器或HttpRequestHandler Spring的預設架構内可以實作此接口,以實作最後修改時間檢查。

注:另類處理的實作方法有不同的最後修改的處理方式。 例如,Spring 2.5的(使用注釋的控制器的方法@RequestMapping )通過提供上次修改支援org.springframework.web.context.request.WebRequest.checkNotModified方法,允許主處理程式方法中最後一次修改檢查。

在用戶端地一次輸入URL時,伺服器端會傳回内容和狀态碼200, 表示請求成功,同時會添加一個“Last-Modified”屬性,表示該請求資源的最後修改時間

用戶端第二次請求此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() 方法

相同的合同為的HttpServlet的getLastModified方法。請求處理之前調用。

傳回值将被發送到HTTP用戶端作為Last-Modified頭,并與如果-Modified-Since的标頭,該用戶端發回。 内容将隻得到再生,如果出現了修改

SpringMVC源碼解析之Last-Modified緩存機制GetLastModified() 方法通路效果原理分析DispatcherServlet.doDispatch()并非MappingHandler都支援緩存

每次修改資源的時候,更新下lastModified的值即可。

通路效果

隻有第一次執行了Controller,以後通路都沒執行Controller。

原理分析DispatcherServlet.doDispatch()

首先擷取 http 請求的method type。

如果method 是 “GET”或“HEAD” 才支援緩存機制通過 HandlerAdapter.getLastModified() 方法擷取 UserCacheController 中的lastModified 的值,最後修改時間。

調用 checkNotModified() 方法驗證 http 請求頭中的“If-Modified-Since”的時間進行對比,判斷頁面是否更新過。如果有更新才執行具體的Controller, 沒有更新則響應 304 狀态碼資訊(HTTP 304: Not Modified )。

getLastModified()

通過handler的擴充卡類,然後在調用UserCacheController.getLastModified() 方法擷取最後更新時間。

checkNotModified()

Paste_Image.png

調用 validateIfModifiedSince() 方法擷取http請求頭中的“If-Modified-Since”值,并驗證是否修改過。

沒有修改過則設定notModified=true,如果修改過則設定notModified=false。

如果 notModified=true,則設定response響應狀态碼304或412

如果是GET 或 HEAD 請求則添加響應頭“Last-Modified”

validateIfModifiedSince()

解析http 請求頭中的“If-Modified-Since”值

判斷緩存頁面是否需要更新。

注:(lastModifiedTimestamp / 1000 * 1000):因為http頭中隻儲存到秒,是以這裡把秒後面的置為0。

HTTP 請求響應頭分析

通過浏覽器F12 可以看出:

每次請求都會攜帶“If-Modified-Since”資訊到伺服器驗證資源是否需要更新。

伺服器響應頭中會包含“Last-Modified”資訊,通路資源最後修改的日期。

緩存限制條件

并非MappingHandler都支援緩存

比如:DefaultAnnotationHandlerMapping 就不支援緩存機制。

因為支援注解的Controller中可以有多個請求方法,而每個方法都需要計算檔案的最後修改時間,這樣LastModified就不适用了。隻适用一個Controller中隻支援一個請求的HandlerMapping。

AnnotationMethodHandlerAdapter

  • 方法永遠傳回-1。
  • SpringMVC源碼解析之Last-Modified緩存機制GetLastModified() 方法通路效果原理分析DispatcherServlet.doDispatch()并非MappingHandler都支援緩存
  • 這個方法總是傳回-1,因為帶注解的控制器可以有許多方法,每個方法需要單獨的上次更改時間的計算。 相反, RequestMapping注解的方法可以計算出上次更改時間值,調用org.springframework.web.context.request.WebRequest.checkNotModified(long) ,以檢查它,并傳回null如果傳回true