?前面的文章已經(jīng)講了從一個請求在spring中的處理過程(從Servlet規(guī)范到FrameworkServlet)代碼及流程圖說明接下來就是,從FrameworkServlet到DispatcherServlet的部分進(jìn)行分析了。
1.從FrameworkServlet到DispatcherServlet
?在FrameworkServlet的processRequest方法中,有調(diào)用doService方法的這個步驟。這個方法是一個抽象方法,必須由子類來實現(xiàn)。這個方法是處理GET, POST, PUT , DELETE請求具體邏輯的方法。
1.1 從FrameworkServlet進(jìn)入到DispatcherServlet的processRequest
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
......
//進(jìn)行業(yè)務(wù)處理
doService(request, response);
......
}
?所以我們可以直接到DispatcherServlet中進(jìn)一步的分析。直接看對應(yīng)的doService方法
1.2 進(jìn)行真正請求處理前的準(zhǔn)備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),因此需要備份當(dāng)前請求(A)
Map<String, Object> attributesSnapshot = null;
//是否是一個include請求,通過request中的javax.servlet.include.request_uri屬性判斷,JSP在運行期間是會被編譯成相應(yīng)的Servlet類來運行的,
// 所以在Servlet中也會有類似的功能和調(diào)用語法,這就是RequestDispatch.include()方法,在一個被別的servlet使用RequestDispatcher的include方法調(diào)用過的servlet中,
// 如果它想知道那個調(diào)用它的servlet的上下文信息該怎么辦呢,那就可以通過request中的attribute中的如下屬性獲?。簀avax.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請求設(shè)定的spring的策略
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
//設(shè)置web應(yīng)用上下文到請求中,
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
//設(shè)置本地解析器
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
//設(shè)置主題解析器
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
//設(shè)置主題,如果沒有設(shè)置則為null,默認(rèn)的為WebApplicationContext
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
//將request跟response保存到一個FlashMap中,F(xiàn)lashMap用來將一個請求跟另外一個請求關(guān)聯(lián)起來,通常在redirect的時候有用
if (this.flashMapManager != null) {
//如果當(dāng)前請求的FlashMap在之前的請求中保存過了,則取出來,并去除對應(yīng)的緩存
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 {
//進(jìn)行請求分發(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);
}
}
}
}
?這里對上面的流程總結(jié)一下:
- 檢查當(dāng)前的請求中是否包含另外的一個請求(也就是請求包含),如果是這種情況需要把內(nèi)部請求的屬性作為快照保存起來。(個人對于這一塊還是有點沒有理解清楚的,歡迎指點)
- 設(shè)置對應(yīng)的web請求上下,本地解析器,主題解析器,主題到request中
- 檢查
flashMapManager是不是null(這個不會為空,在initFlashMapManager方法初始化的時候回創(chuàng)建),如果不是空則吧對應(yīng)的request,respons設(shè)置進(jìn)一個FlashMap對象中,同時設(shè)置對應(yīng)的輸入,輸出跟flashMap管理對象到request中 - 進(jìn)行請求分發(fā)處理
doDispatch方法 - 最后處理異步請求處理相關(guān)的邏輯,主要檢查這個請求的異步處理邏輯是不是正在處理,是的就將上面保存的內(nèi)部請求設(shè)置到
request中。
?這里涉及到請求包含(include)跟請求轉(zhuǎn)發(fā)(forward)。關(guān)于這兩個東西可以百度一下或者參考下面這個文章請求包含(Include)和請求轉(zhuǎn)發(fā)(Forward)這個位置理解有點費力。個人理解可能也有點缺陷,歡迎指正。
?在準(zhǔn)備好了處理請求需要的相關(guān)環(huán)境跟參數(shù)的之后,就是進(jìn)行請求的分發(fā)處理了,之所以叫分發(fā)是因為每個請求都有對應(yīng)的處理類,現(xiàn)在進(jìn)入到分發(fā)的邏輯方法doDispatch
1.3 進(jìn)行請求分發(fā)的doDispatch
?因為doDispatch這個方法的內(nèi)部邏輯比較復(fù)雜(方法內(nèi)部調(diào)用了其他的復(fù)雜的方法邏輯),一篇博客肯定寫不完,所以這里主要寫整個方法內(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請求并將請求轉(zhuǎn)化為MultipartHttpServletRequest類型的
processedRequest = checkMultipart(request);
//如果請求不是原來的request請求,則表示是multipart請求并且解析過的
multipartRequestParsed = (processedRequest != request);
//獲取封裝了HandlerInterceptor的HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
//如果不存在對應(yīng)的處理鏈則返回
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)) {
//檢查當(dāng)前的get類型的請求的最后修改時間是不是存在的,這個參數(shù)用來減少數(shù)據(jù)傳輸用
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
//如果瀏覽器請求中的最后請求時間跟服務(wù)器的最后修改時間一致,并且是get類型請求則直接返回。關(guān)于lastModified這個會說明
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//調(diào)用mappedHandler中攔截器的applyPreHandle方法,如果對應(yīng)的請求不符合規(guī)則則直接返回
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//執(zhí)行對應(yīng)的HandlerAdapter的Handler方法,拿到對應(yīng)的視圖
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//檢查當(dāng)前的請求是否正在異步處理,如果是的則直接放棄并返回
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//如果處理的結(jié)果返回的視圖是空的則使用默認(rèn)的視圖,不為空則用處理的結(jié)果
applyDefaultViewName(processedRequest, mv);
//調(diào)用applyPostHandle方法對視圖進(jìn)行返回后的處理
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
//進(jìn)行錯誤視圖的處理
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 {
//檢查當(dāng)前的請求是否正在異步處理
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
//如果mappedHandler不是null,則調(diào)用對應(yīng)的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)上的一張圖具體說明
- 首先會檢查當(dāng)前的請求是不是
multipart/form-data類型的請求,是的則將請求進(jìn)行轉(zhuǎn)換為MultipartHttpServletRequest類型的request。(MultipartHttpServletRequest是HttpServletRequest的子類) - 獲取合適的獲取封裝了
HandlerInterceptor集合的HandlerExecutionChain,如果沒有找到合適的,就直接返回404的錯誤頁面 - 從
HandlerExecutionChain中獲取Handler然后尋找合適的HandlerAdapter - 檢查是不是
get請求,是的然后在檢查lastModified這個屬性選擇是直接返回還是進(jìn)行后續(xù)的請求處理。(lastModified這個屬性后面說一下,其實前面文章已經(jīng)說過了) - 調(diào)用前面選擇的
HandlerAdapter的applyPreHandle方法檢查請求是否符合定義的要求,不符合就返回。 - 調(diào)用前面選擇的
HandlerAdapter的handle方法,進(jìn)行邏輯的處理,然后返回ModelAndView對象 - 檢查當(dāng)前的請求是否正在異步處理,如果是的則直接放棄并返回
- 檢查返回的
ModelAndView的視圖是不是null,是的則返回默認(rèn)的,不是的則不處理 - 調(diào)用前面選擇的
HandlerAdapter的applyPostHandle方法對視圖進(jìn)行最后的處理 - 對正常返回的或者發(fā)生異常時生成的視圖進(jìn)行處理,
- 對異步請求或
multipart/form-data類型請求進(jìn)行后續(xù)的處理
?對于lastModified這個屬性可以參考這篇文章HttpServlet---getLastModified與緩存
后續(xù)的
?雖然整個的request請求的邏輯只有這么多,但是里面的每個步驟都是比較復(fù)雜的。這一篇講不完,因此挑選里面比較重要的幾個步驟進(jìn)行分篇講解。主要以下幾個步驟