?前面的文章已經(jīng)講了從一個請求在spring中的處理過程(從Servlet規(guī)范到FrameworkServlet)代碼及流程圖說明接下來就是壳影,從FrameworkServlet
到DispatcherServlet
的部分進行分析了腰池。
1.從FrameworkServlet
到DispatcherServlet
?在FrameworkServlet
的processRequest
方法中,有調用doService
方法的這個步驟撩轰。這個方法是一個抽象方法鸣戴,必須由子類來實現(xiàn)啃沪。這個方法是處理GET, POST, PUT , DELETE
請求具體邏輯的方法。
1.1 從FrameworkServlet
進入到DispatcherServlet
的processRequest
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
......
//進行業(yè)務處理
doService(request, response);
......
}
?所以我們可以直接到DispatcherServlet
中進一步的分析窄锅。直接看對應的doService
方法
1.2 進行真正請求處理前的準備doService
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include, to be able to restore the original attributes after the include.
//如果是一個include請求创千,<jsp:incluede page="xxx.jsp"/> 這種中,可能在一個請求(A)中嵌套了另外的一個請求(B)入偷,因此需要備份當前請求(A)
Map<String, Object> attributesSnapshot = null;
//是否是一個include請求追驴,通過request中的javax.servlet.include.request_uri屬性判斷,JSP在運行期間是會被編譯成相應的Servlet類來運行的疏之,
// 所以在Servlet中也會有類似的功能和調用語法殿雪,這就是RequestDispatch.include()方法,在一個被別的servlet使用RequestDispatcher的include方法調用過的servlet中,
// 如果它想知道那個調用它的servlet的上下文信息該怎么辦呢锋爪,那就可以通過request中的attribute中的如下屬性獲缺铩:javax.servlet.include.request_uri
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
//獲取包含的請求中的獲取A請求的內(nèi)部B請求設定的spring的策略
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
//設置web應用上下文到請求中,
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
//設置本地解析器
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
//設置主題解析器
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
//設置主題其骄,如果沒有設置則為null,默認的為WebApplicationContext
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
//將request跟response保存到一個FlashMap中亏镰,F(xiàn)lashMap用來將一個請求跟另外一個請求關聯(lián)起來,通常在redirect的時候有用
if (this.flashMapManager != null) {
//如果當前請求的FlashMap在之前的請求中保存過了拯爽,則取出來索抓,并去除對應的緩存
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
//保存FlashMap到屬性中
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 {
//進行請求分發(fā)處理
doDispatch(request, response);
}
finally {
//在FrameworkServlet中會生成WebAsyncManager
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
?這里對上面的流程總結一下:
- 檢查當前的請求中是否包含另外的一個請求(也就是請求包含),如果是這種情況需要把內(nèi)部請求的屬性作為快照保存起來。(個人對于這一塊還是有點沒有理解清楚的纸兔,歡迎指點)
- 設置對應的web請求上下惰瓜,本地解析器,主題解析器汉矿,主題到request中
- 檢查
flashMapManager
是不是null(這個不會為空崎坊,在initFlashMapManager
方法初始化的時候回創(chuàng)建),如果不是空則吧對應的request
,respons
設置進一個FlashMap
對象中洲拇,同時設置對應的輸入奈揍,輸出跟flashMap
管理對象到request
中 - 進行請求分發(fā)處理
doDispatch
方法 - 最后處理異步請求處理相關的邏輯,主要檢查這個請求的異步處理邏輯是不是正在處理赋续,是的就將上面保存的內(nèi)部請求設置到
request
中男翰。
?這里涉及到請求包含(include)跟請求轉發(fā)(forward)。關于這兩個東西可以百度一下或者參考下面這個文章請求包含(Include)和請求轉發(fā)(Forward)這個位置理解有點費力纽乱。個人理解可能也有點缺陷蛾绎,歡迎指正。
?在準備好了處理請求需要的相關環(huán)境跟參數(shù)的之后鸦列,就是進行請求的分發(fā)處理了租冠,之所以叫分發(fā)是因為每個請求都有對應的處理類,現(xiàn)在進入到分發(fā)的邏輯方法doDispatch
1.3 進行請求分發(fā)的doDispatch
?因為doDispatch
這個方法的內(nèi)部邏輯比較復雜(方法內(nèi)部調用了其他的復雜的方法邏輯)薯嗤,一篇博客肯定寫不完顽爹,所以這里主要寫整個方法內(nèi)的邏輯。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
//從request中獲取WebAsyncManager
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//檢查是不是multipart請求并將請求轉化為MultipartHttpServletRequest類型的
processedRequest = checkMultipart(request);
//如果請求不是原來的request請求骆姐,則表示是multipart請求并且解析過的
multipartRequestParsed = (processedRequest != request);
//獲取封裝了HandlerInterceptor的HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
//如果不存在對應的處理鏈則返回
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//從HandlerExecutionChain中獲取Handler然后尋找合適的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//獲取請求方式
String method = request.getMethod();
boolean isGet = "GET".equals(method);
//檢查是不是GET請求
if (isGet || "HEAD".equals(method)) {
//檢查當前的get類型的請求的最后修改時間是不是存在的镜粤,這個參數(shù)用來減少數(shù)據(jù)傳輸用
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
//如果瀏覽器請求中的最后請求時間跟服務器的最后修改時間一致,并且是get類型請求則直接返回玻褪。關于lastModified這個會說明
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//調用mappedHandler中攔截器的applyPreHandle方法肉渴,如果對應的請求不符合規(guī)則則直接返回
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//執(zhí)行對應的HandlerAdapter的Handler方法,拿到對應的視圖
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//檢查當前的請求是否正在異步處理归园,如果是的則直接放棄并返回
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//如果處理的結果返回的視圖是空的則使用默認的視圖黄虱,不為空則用處理的結果
applyDefaultViewName(processedRequest, mv);
//調用applyPostHandle方法對視圖進行返回后的處理
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
//進行錯誤視圖的處理
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//對正常視圖或者錯誤視圖的處理
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
//檢查當前的請求是否正在異步處理
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
//如果mappedHandler不是null稚矿,則調用對應的mappedHandler中的AsyncHandlerInterceptor
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
//對流類型的請求庸诱,做后置的處理
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
?對于doDispatch
方法的處理流程就跟網(wǎng)上的mvc處理流程圖一樣的,把整個流程包含在了一個方法里面晤揣。這里就用網(wǎng)上的一張圖具體說明
- 首先會檢查當前的請求是不是
multipart/form-data
類型的請求桥爽,是的則將請求進行轉換為MultipartHttpServletRequest
類型的request。(MultipartHttpServletRequest
是HttpServletRequest
的子類) - 獲取合適的獲取封裝了
HandlerInterceptor
集合的HandlerExecutionChain
昧识,如果沒有找到合適的钠四,就直接返回404的錯誤頁面 - 從
HandlerExecutionChain
中獲取Handler然后尋找合適的HandlerAdapter
- 檢查是不是
get
請求,是的然后在檢查lastModified
這個屬性選擇是直接返回還是進行后續(xù)的請求處理。(lastModified
這個屬性后面說一下缀去,其實前面文章已經(jīng)說過了) - 調用前面選擇的
HandlerAdapter
的applyPreHandle
方法檢查請求是否符合定義的要求侣灶,不符合就返回。 - 調用前面選擇的
HandlerAdapter
的handle
方法缕碎,進行邏輯的處理褥影,然后返回ModelAndView
對象 - 檢查當前的請求是否正在異步處理,如果是的則直接放棄并返回
- 檢查返回的
ModelAndView
的視圖是不是null
咏雌,是的則返回默認的凡怎,不是的則不處理 - 調用前面選擇的
HandlerAdapter
的applyPostHandle
方法對視圖進行最后的處理 - 對正常返回的或者發(fā)生異常時生成的視圖進行處理,
- 對異步請求或
multipart/form-data
類型請求進行后續(xù)的處理
?對于lastModified
這個屬性可以參考這篇文章HttpServlet---getLastModified與緩存
后續(xù)的
?雖然整個的request
請求的邏輯只有這么多赊抖,但是里面的每個步驟都是比較復雜的统倒。這一篇講不完,因此挑選里面比較重要的幾個步驟進行分篇講解氛雪。主要以下幾個步驟