Springソースコード分析の派生物10:最後に変更されたキャッシュメカニズム

I.はじめに

この記事はSpringソースコード分析の派生記事です。Springソースコード分析21:SpringMVC③DispatcherServletロジック主な理由は、ソースコードを分析する過程で他のコンテンツを理解できなかったため、学習用のコンテンツを改善するために派生記事を開きました。


春のコレクションのカタログ:春のソース分析:コレクションの完成


このシリーズのカタログは次のとおりです。

  1. Springソースコード分析19:SpringMVC①ビルド
  2. Springソースコード分析20:SpringMVC②DispatcherServletの初期化
  3. Springソースコード分析21:SpringMVC③DispatcherServletロジック

派生記事のリストは次のとおりです。

  1. Springソースコード分析の派生物10:最後に変更されたキャッシュメカニズム
  2. Springソースコード分析派生11:HandlerMapping

二、最後に変更

次のコンテンツはBaidu百科事典からのものです

  1. ブラウザが初めてURLを要求すると、サーバー側のリターンステータスは200になり、コンテンツはクライアントから要求されたリソースになります。同時に、Last-Modified属性があり、ファイルはサーバー側で最後に変更されました。最終変更フォーマットは次のようになります。最終変更:2006年5月12日金曜日18:53:33 GMT

  2. クライアントがこのURLを2回要求すると、HTTPプロトコルの規定に従って、ブラウザはIf-Modified-Sinceヘッダーをサーバーに送信し、この時間以降にファイルが変更されたかどうかを確認します。If-Modified-開始日:2006年5月12日金曜日18:53:33 GMT
    サーバー側のリソースが変更されていない場合、HTTP 304(変更なし)ステータスコードが自動的に返され、コンテンツが空になり、データ量が節約されます。転送されました。サーバー側のコードが変更されるか、サーバーが再起動されると、リソースが再発行され、戻り値は最初の要求と同様になります。これにより、リソースがクライアントに繰り返し送信されることがなくなり、サーバーが変更されたときにクライアントが最新のリソースを取得できるようになります。

三、実現計画

1.org.springframework.web.servlet.mvc.LastModifiedインターフェースを実装します

1.1。簡単なデモンストレーション

このスキームは、通常org.springframework.web.servlet.mvc.Controller、次のように、コントローラーシーンインターフェイスを実現するために使用されます。

@Component("/beanNameSay")
public class BeanNameSayController implements Controller, LastModified {
    
    
    private long lastModified;
	// 逻辑处理
    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
    
    
        return new ModelAndView("/hello.html");
    }

    @Override
    public long getLastModified(HttpServletRequest request) {
    
    
    	// 如果 lastModified  刚初始化,则赋值为当前时间戳并返回。
        if (lastModified == 0L){
    
    
            lastModified = System.currentTimeMillis();
        }
        return lastModified;
    }
}

1つの要求が成功すると、2番目の要求に対してステータスコード304が返されます。
ここに画像の説明を挿入

1.2。主成分分析

春のソースコードの解析21:Spring MVCの③のDispatcherServletロジック、我々は最終-Modifiedのキャッシュメカニズムを述べました。この記事では、この点に焦点を当てます

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    
		...
		// 获取请求方式
		String method = request.getMethod();
		boolean isGet = "GET".equals(method);
		// 如果是 get请求或者 head 请求则进入该分支
		if (isGet || "HEAD".equals(method)) {
    
    
			// 调用 HandlerAdapter#getLastModified 方法 来获取最后修改时间
			long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
			// 判断到目前为止是否有过修改,没有则直接return。实现缓存的功能
			if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
    
    
				return;
			}
		}
		...
	}

私たちはここに焦点を当てる必要がある以上二つの方法よりも何もないHandlerAdapter#getLastModifiedServletWebRequest#checkNotModified(long)一つずつ見ていきましょう

1.2.1 HandlerAdapter#getLastModified

HandlerAdapter#getLastModified異なるHandlerAdapter我々がここで使用されているため、異なる実装を持つBeanNameUrlHandlerMappingプロセッサマッピングモード。したがって、ここで一致するHandlerAdapterメソッドは次のとおりです。SimpleControllerHandlerAdapter#getLastModified

ここに画像の説明を挿入

SimpleControllerHandlerAdapter#getLastModified 具体的な実装は次のとおりです。

	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
    
    
		// 如果handler 是 LastModified 的 实现类。则直接调用 handler 的 getLastModified 方法
		if (handler instanceof LastModified) {
    
    
			return ((LastModified) handler).getLastModified(request);
		}
		// 否则返回-1
		return -1L;
	}

これは非常に明確です。これはBeanNameSayController#getLastModified、最終変更時刻を取得するために呼び出されます。ここで達成するのは、最初のリクエストで保持されるタイムスタンプです。

ここに画像の説明を挿入

1.2.2 ServletWebRequest#checkNotModified(long)

上記の分析の結果、ha.getLastModified(request, mappedHandler.getHandler());呼び出し要求によって返される値は、予約時のタイムスタンプを初めて返すときであることがわかります。

ServletWebRequest#checkNotModified(long)検証の実行方法を見てみましょう

	@Override
	public boolean checkNotModified(long lastModifiedTimestamp) {
    
    
		return checkNotModified(null, lastModifiedTimestamp);
	}
	
	@Override
	public boolean checkNotModified(@Nullable String etag, long lastModifiedTimestamp) {
    
    
		HttpServletResponse response = getResponse();
		// notModified  为true 标志没有被修改,默认false
		// 如果 notModified  已经true || 返回状态码已经不是200直接返回 notModified 
		if (this.notModified || (response != null && HttpStatus.OK.value() != response.getStatus())) {
    
    
			return this.notModified;
		}

		// Evaluate conditions in order of precedence.
		// See https://tools.ietf.org/html/rfc7232#section-6
		// 解析校验 If-Unmodified-Since 请求头。这个请求头和  If-Modified-Since 请求头相反
		if (validateIfUnmodifiedSince(lastModifiedTimestamp)) {
    
    
			// 设置状态码 304,并返回 notModified
			if (this.notModified && response != null) {
    
    
				response.setStatus(HttpStatus.PRECONDITION_FAILED.value());
			}
			return this.notModified;
		}

		// 校验 If-None-Match 请求头。这是针对 Etag 缓存。
		boolean validated = validateIfNoneMatch(etag);
		if (!validated) {
    
    
			// 校验 If-Modified-Since 请求头
			validateIfModifiedSince(lastModifiedTimestamp);
		}

		// Update response
		// 更新 Response。包括状态码等信息
		if (response != null) {
    
    
			boolean isHttpGetOrHead = SAFE_METHODS.contains(getRequest().getMethod());
			if (this.notModified) {
    
    
				response.setStatus(isHttpGetOrHead ?
						HttpStatus.NOT_MODIFIED.value() : HttpStatus.PRECONDITION_FAILED.value());
			}
			if (isHttpGetOrHead) {
    
    
				if (lastModifiedTimestamp > 0 && parseDateValue(response.getHeader(HttpHeaders.LAST_MODIFIED)) == -1) {
    
    
					response.setDateHeader(HttpHeaders.LAST_MODIFIED, lastModifiedTimestamp);
				}
				if (StringUtils.hasLength(etag) && response.getHeader(HttpHeaders.ETAG) == null) {
    
    
					response.setHeader(HttpHeaders.ETAG, padEtagIfNecessary(etag));
				}
			}
		}

		return this.notModified;
	}

	// 解析校验 If-Modified-Since 请求头
	private boolean validateIfModifiedSince(long lastModifiedTimestamp) {
    
    
		if (lastModifiedTimestamp < 0) {
    
    
			return false;
		}
		long ifModifiedSince = parseDateHeader(HttpHeaders.IF_MODIFIED_SINCE);
		if (ifModifiedSince == -1) {
    
    
			return false;
		}
		// We will perform this validation...
		this.notModified = ifModifiedSince >= (lastModifiedTimestamp / 1000 * 1000);
		return true;
	}

ここで何が起こっているのかを知ることができます

  1. ブラウザが初めて要求しますが、すべて正常です。SimpleControllerHandlerAdapter#getLastModified現在のリクエストのタイムスタンプを保存し、タイムスタンプLast-Modifiedまでにヘッドに応じてブラウザに戻します。
  2. ブラウザの2番目のリクエストは、If-Modified-Since最後に取得しリクエストのヘッドバンドを使用Last-Modifiedます。ではDispatcherServletプロセスの呼び出しHandlerAdapter#getLastModifiedの最初のステップのタイムスタンプのホールドを取得するにはlastModified、タイムスタンプは、最後の通話時間のタイムスタンプです。
  3. WebRequest#checkNotModified(long)次の3つのリクエストヘッダーをチェックして、リクエストが変更されているかどうかを判断する方法:
    - If-Unmodified-SinceIf-Modified-Since代わりに、最後に変更された日付以降に指定されていない場合。指定された日付以降に要求が変更された場合、応答は412(前提条件の失敗)エラーになります。
    - If-None-Match:ETagのキャッシュ。指定されたリソースに一致するETagがない場合、サーバーは要求されたリソースをステータスとともに返します。他の方法の場合、リクエストは、最終的な既存のリソースETagがリストされた値のいずれにも適合しない場合にのみ処理されます。
    - If-Modified-Since:指定された日付以降に最後に変更された場合のみ。リクエストが変更されていない場合、応答は本文なしで304になります。Last-Modifiedヘッダーには、最後に変更された日付が含まれます。If-Unmodified-Sinceとは異なり、If-Modified-SinceはGETまたはHEADでのみ使用できます。

2. UsingWebRequest#checkNotModified(long)

私たちがために@RequestMapping("say")変更するために注釈モード要求を介して達成することができませんorg.springframework.web.servlet.mvc.LastModified。この関数インタフェースを実現します。具体的な理由については後で説明します。

ここではWebRequest#checkNotModified(long)、実現によってのみ電話をかけることができます

2.1。簡単なデモンストレーション

@RestController
@RequestMapping("say")
public class SayController{
    
    
    private long lastModified;

    @RequestMapping("hello")
    public String hello(WebRequest webRequest) {
    
    
        if(webRequest.checkNotModified(lastModified)){
    
    
            return null;
        }
        lastModified = System.currentTimeMillis();
        return "hello";
    }
}

ここに画像の説明を挿入

2.2。主成分分析

直接実現org.springframework.web.servlet.mvc.LastModifiedできない理由を見てみましょうまたはDispatcherServlet#doDispatch場所の前に

ここに画像の説明を挿入
次のようHandlerAdapterに、HandlerAdapter型がRequestMappingHandlerAdapterである場所は異なる場所を見ることができRequestMappingHandlerAdapter#getLastModifiedメソッドがメソッドを呼び出す場合のみですRequestMappingHandlerAdapter#getLastModifiedInternal

	@Override
	protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {
    
    
		return -1;
	}

-1が直接返されることがわかります。つまり、この方法でlastModifiedを変更することはできません。
したがって、上記のhelloの文言によって、WebRequest#checkNotModified(long)judgeメソッドによって直接呼び出された場合WebRequest#checkNotModified(long)メソッドのロジックはここでは繰り返されません。

4、他のキャッシュ方法をHttp

推奨読書:
Last-Modify、ETag、Expires、Cache-Control


上:コンテンツ
については、Last-Modify、ETag、Expires、およびCache-Control
Tencent Httpチュートリアル参照してください。
侵入がある場合は、連絡して削除してください。コンテンツは、自己記録と学習にのみ使用されます。エラーがあれば訂正してください

おすすめ

転載: blog.csdn.net/qq_36882793/article/details/109515781