記事のディレクトリ
I.はじめに
この記事は、Springソースコード分析の派生記事です。Springソースコード分析21:SpringMVC③DispatcherServletロジック。主な理由は、ソースコードを分析する過程で他のコンテンツを理解できなかったため、学習用のコンテンツを改善するために派生記事を開きました。
春のコレクションのカタログ:春のソース分析:コレクションの完成
このシリーズのカタログは次のとおりです。
- Springソースコード分析19:SpringMVC①ビルド
- Springソースコード分析20:SpringMVC②DispatcherServletの初期化
- Springソースコード分析21:SpringMVC③DispatcherServletロジック
派生記事のリストは次のとおりです。
二、最後に変更
次のコンテンツはBaidu百科事典からのものです
-
ブラウザが初めてURLを要求すると、サーバー側のリターンステータスは200になり、コンテンツはクライアントから要求されたリソースになります。同時に、Last-Modified属性があり、ファイルはサーバー側で最後に変更されました。最終変更フォーマットは次のようになります。最終変更:2006年5月12日金曜日18:53:33 GMT
-
クライアントがこの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#getLastModified
とServletWebRequest#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;
}
ここで何が起こっているのかを知ることができます
- ブラウザが初めて要求しますが、すべて正常です。
SimpleControllerHandlerAdapter#getLastModified
現在のリクエストのタイムスタンプを保存し、タイムスタンプLast-Modified
までにヘッドに応じてブラウザに戻します。 - ブラウザの2番目のリクエストは、
If-Modified-Since
最後に取得したリクエストのヘッドバンドを使用しLast-Modified
ます。ではDispatcherServlet
プロセスの呼び出しHandlerAdapter#getLastModified
の最初のステップのタイムスタンプのホールドを取得するにはlastModified
、タイムスタンプは、最後の通話時間のタイムスタンプです。 WebRequest#checkNotModified(long)
次の3つのリクエストヘッダーをチェックして、リクエストが変更されているかどうかを判断する方法:
-If-Unmodified-Since
:If-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チュートリアルを参照してください。
侵入がある場合は、連絡して削除してください。コンテンツは、自己記録と学習にのみ使用されます。エラーがあれば訂正してください