DispatcherServletの本体編
●ソース
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
//これから必要な部品をrequestに入れる
//web.xmlで設定したカスタマイズ用プロパティをrequestに入れる
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
//国際化用のresolverを設定(SessionLocaleResolver、CookieLocaleResolverなど)
//localeResolverはinitLocaleResolverで設定され、SpringのBeanコンテナから「localResolver」名前で検索し、
//見つけなかったら、defaultStrategyで設定する
//DefaultStrategyはorg.springframework.web.servlet.DispatcherServlet.propertiesから取り込む
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
//localeResolverに似てて、テームをアレンジできるように(SessionThemeResolver、CookieThemeResolverなど)
//テームは簡単に言うと、夏の時に、こうする、冬の時に、そうするをできるように
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
//FlashMapは二重送信回避用PRG構造に役たつ、ようするに、Mapにデータを登録しておくと一回のHTTPリダイレクトの最中のみデータが保持され、次回以降のリクエスト時までに自動的に削除されること
//ソースを読む限り、この中間データはセッションまたはほかの永続化場所に保存することができる、AbstractFlashMapManagerを継承すればOK、
//一般的に、SessionFlashMapManagerをそのまま利用するでしょうか、ようするに、「SessionFlashMapManager.FLASH_MAPS」をキーにしてSessionに保存する
//実際の処理として①セッションからすべてのFlashMapを取得する②有効期限切れのFlashMapを削除③request pathとquery parameterでcurrent requestにマッチするFlashMapを取得
//falshMapの有効期限はAbstractFlashMapManagerの中で、flashMapTimeout = 180秒デフォルトとして定義しています。
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
//実際のHandler処理はここから
doDispatch(request, response);
} finally {
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
return;
}
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
//非同期を管理するやつを取得しておく
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//spring容器の中で、multipartResolver有無をチェックして、ある場合、multipartResolverのisMultipartをreqeustをチェックする
//multipartの場合、HttpServletRequestのrequestをMultipartHttpServletRequestに変換する
processedRequest = checkMultipart(request);
//オブジェクト間の!=のチェックは普段あまり使わないんですが、ここのメリットはcheckMultipart戻り値は変換後のオブジェクトだけOKのことですね。
multipartRequestParsed = (processedRequest != request);
//事前に生成したthis.handlerMappingsからrequest情報にマッチするHandlerを取得
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
//リクエストハンドラーを特定のインターフェースに適合させる
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
//ここはLash-Modifiedのキャッシュアルゴリズムについて話します
//①URLでリクエスト、200を返しながら、リクエスト対象ファイルがサーバー上の最後の更新日時を表すLast-Modifiedを追加 例:“Last-Modified:Wed, 14 Mar 2012 10:22:42 GMT”
//②二回目同じURLでリクエスト場合、ヘッダーにif-Modified-Sinceで該当資源が更新されたかどうかを質問しておく 例:“If-Modified-Since: Wed, 14 Mar 2012 10:22:42 GMT”
//変化がなければ、HTTP 304を返す
//springの中で、Last-Modifiedを使いたい場合、該当ControllerはLastModifiedを実装すればできる、(getLastModifiedのみ)
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//HandlerInterceptor処理のため
//preHandle() Handlerの実行前
//postHandle() Handlerの実行後
//afterCompletion() すべてのリクエスト処理完了後(viewの生成の後)
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
//実際のリクエストを処置して、viewを貰う
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
applyDefaultViewName(request, mv);
//HandlerInterceptor処理のため
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
}
//処理結果がModelAndViewまたはExceptionによって、結果を生成する
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
} catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}}}