Spring MVC學(xué)習(xí)之DispatcherServlet請(qǐng)求處理詳析
前言
要深入理解spring mvc的工作流程,就需要先了解spring mvc的架構(gòu):

從上圖可以看到 前端控制器DispatcherServlet在其中起著主導(dǎo)作用,理解了DispatcherServlet 就完全可以說(shuō)弄清楚了spring mvc。
DispatcherServlet作為Spring用于處理web請(qǐng)求注冊(cè)的唯一一個(gè)Servlet,所有的請(qǐng)求都是經(jīng)由DispatcherServlet進(jìn)行分發(fā)處理的。本文主要講解DispatcherServlet是如何對(duì)請(qǐng)求進(jìn)行分發(fā),處理,并且生成相應(yīng)的視圖的。
1. 整體結(jié)構(gòu)
在HttpServlet中,其對(duì)不同方式的請(qǐng)求進(jìn)行了分發(fā),比如對(duì)于GET請(qǐng)求,其提供了doGet()方法,對(duì)于POST請(qǐng)求,其提供了doPost()方法等等。通過(guò)這種方式,子類可以針對(duì)于當(dāng)前請(qǐng)求的方式實(shí)現(xiàn)不同的方法即可。但是在DispatcherServlet中,由于需要使用同一的方式對(duì)不同的請(qǐng)求進(jìn)行處理,因而其對(duì)各個(gè)請(qǐng)求方式進(jìn)行了整合,如下就是DispatcherServlet針對(duì)GET和POST請(qǐng)求所編寫(xiě)的同一處理邏輯:
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
可以看到,無(wú)論是GET請(qǐng)求還是POST請(qǐng)求,DispatcherServlet都是委托給了processRequest()方法處理,對(duì)于其他的請(qǐng)求方式,其處理方式也是類似的。通過(guò)這種方式,DispatcherServlet將各個(gè)請(qǐng)求整合在了一起,雖然整合在了一起,但是request中也還是保存有當(dāng)前請(qǐng)求的請(qǐng)求方式的,因而保存了后續(xù)對(duì)請(qǐng)求進(jìn)行分發(fā)的能力。這里我們直接看processRequest()方法是如何處理各個(gè)請(qǐng)求的:
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// 獲取先前請(qǐng)求的LocaleContext
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
// 獲取當(dāng)前請(qǐng)求的LocaleContext,其中保存了當(dāng)前請(qǐng)求的Locale信息
LocaleContext localeContext = buildLocaleContext(request);
// 獲取先前請(qǐng)求的Attributes信息
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
// 獲取當(dāng)前請(qǐng)求的Attributes信息,其中保存了當(dāng)前請(qǐng)求的各個(gè)屬性數(shù)據(jù)
ServletRequestAttributes requestAttributes =
buildRequestAttributes(request, response, previousAttributes);
// 獲取當(dāng)前請(qǐng)求的WebAsyncManager,這只有在當(dāng)前請(qǐng)求是請(qǐng)求的異步任務(wù)時(shí)才會(huì)真正用到
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
// 注冊(cè)異步任務(wù)的攔截器,如果請(qǐng)求的是異步任務(wù),這個(gè)攔截器可以攔截異步任務(wù)的前置,后置和異常等情況
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(),
new RequestBindingInterceptor());
// 將當(dāng)前請(qǐng)求的Locale,Attributes信息初始化到對(duì)應(yīng)的ThreadLocal對(duì)象中,用于后續(xù)使用
initContextHolders(request, localeContext, requestAttributes);
try {
// 對(duì)當(dāng)前請(qǐng)求進(jìn)行分發(fā)
doService(request, response);
} catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
} catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
} finally {
// 在請(qǐng)求完成之后,判斷當(dāng)前請(qǐng)求的Locale和Attributes信息是否需要繼承,如果需要繼承,
// 則會(huì)將Locale信息設(shè)置到inheritableLocaleContextHolder中,而將Attributes
// 信息設(shè)置到inheritableRequestAttributesHolder中;否則就會(huì)移除對(duì)應(yīng)的信息,
// 而只為當(dāng)前請(qǐng)求的ContextHolder設(shè)置相應(yīng)的屬性
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
// 調(diào)用已注冊(cè)的在當(dāng)前請(qǐng)求被銷毀時(shí)的回調(diào)函數(shù),并且更新Session中當(dāng)前請(qǐng)求所更新的屬性
requestAttributes.requestCompleted();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
} else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
} else {
this.logger.debug("Successfully completed request");
}
}
}
// 發(fā)布請(qǐng)求已經(jīng)完成的事件,以便對(duì)該事件進(jìn)行監(jiān)聽(tīng)的程序進(jìn)行相應(yīng)的處理
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
可以看到,processRequest()方法主要是對(duì)Locale和Attributes信息進(jìn)行了處理,然后就通過(guò)doService()方法對(duì)請(qǐng)求再次進(jìn)行了分發(fā)。我們這里繼續(xù)閱讀doService()方法的源碼:
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult()
? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed
+ " processing " + request.getMethod() + " request for ["
+ getRequestUri(request) + "]");
}
// 這里主要是判斷當(dāng)前請(qǐng)求是否為include請(qǐng)求,如果是include請(qǐng)求,那么就會(huì)將當(dāng)前請(qǐng)求中的
// 數(shù)據(jù)都放入一個(gè)快照中,在當(dāng)前請(qǐng)求完成之后,會(huì)從該塊中中取出數(shù)據(jù),然后將其重新加載到
// 當(dāng)前request中,以便request進(jìn)行后續(xù)的處理。這里默認(rèn)情況下是會(huì)對(duì)所有的屬性進(jìn)行處理的,
// 因?yàn)閏leanupAfterInclude默認(rèn)值為true,如果將其設(shè)置為false,那么就只會(huì)對(duì)Spring框架
// 相關(guān)的屬性進(jìn)行處理
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude
|| attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// 這里分別將ApplicationContext,LoacleResolver,ThemeResolver和ThemeSource等
// bean添加到當(dāng)前request中
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
// 這里FlashMapManager主要的作用在于當(dāng)請(qǐng)求如果是重定向的請(qǐng)求,那么可以將一些屬性保存在FlashMap
// 中,然后通過(guò)FlashMapManager進(jìn)行管理,從而在重定向之后能夠獲取到重定向之前所保存的請(qǐng)求
if (this.flashMapManager != null) {
// 在當(dāng)前請(qǐng)求中獲取FlashMap數(shù)據(jù),如果不是重定向之后的請(qǐng)求,那么這里獲取到的就是空值
FlashMap inputFlashMap =
this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
// 將獲取到的FlashMap數(shù)據(jù)保存在request中
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE,
Collections.unmodifiableMap(inputFlashMap));
}
// 設(shè)置默認(rèn)的FlashMap和FlashMapManager
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
// 這里才是真正的對(duì)請(qǐng)求進(jìn)行分發(fā)處理的位置
doDispatch(request, response);
} finally {
// 判斷當(dāng)前請(qǐng)求不是一個(gè)異步任務(wù)的請(qǐng)求,但是是一個(gè)include請(qǐng)求,那么就會(huì)重新加載
// 請(qǐng)求之前保存的快照數(shù)據(jù)
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
這里的doService()方法也還沒(méi)有對(duì)請(qǐng)求進(jìn)行真正的處理,其首先判斷了當(dāng)前請(qǐng)求是不是一個(gè)include請(qǐng)求,如果是include請(qǐng)求,那么就將請(qǐng)求的屬性都保存在一個(gè)快照中,以便請(qǐng)求完成之后能夠重新進(jìn)行加載;然后會(huì)判斷當(dāng)前是否是一個(gè)重定向之后的請(qǐng)求,如果是重定向之后的請(qǐng)求,那么其FlashMapManager就不是空的,此時(shí)會(huì)將重定向之前保存的屬性重新加載到當(dāng)前請(qǐng)求中;最后doService()方法才會(huì)調(diào)用doDispatch()方法進(jìn)行請(qǐng)求的分發(fā)和處理。如下是doDispatch()方法的源碼:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
// 獲取當(dāng)前的異步任務(wù)管理器
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 這里判斷當(dāng)前請(qǐng)求是否為一個(gè)文件請(qǐng)求,這里的判斷方式就是要求當(dāng)前請(qǐng)求滿足兩點(diǎn):①請(qǐng)求
// 方式是POST;②判斷contentType是否以multipart/開(kāi)頭。如果滿足這兩點(diǎn),那么就認(rèn)為當(dāng)前
// 請(qǐng)求是一個(gè)文件請(qǐng)求,此時(shí)會(huì)將當(dāng)前請(qǐng)求的request對(duì)象封裝為一個(gè)
// MultipartHttpServletRequest對(duì)象,這也是我們?cè)诙x文件請(qǐng)求的Controller時(shí)
// 能夠?qū)equest參數(shù)寫(xiě)為MultipartHttpServletRequest的原因。這里如果不是文件請(qǐng)求,
// 那么會(huì)將request直接返回。
processedRequest = checkMultipart(request);
// 這里判斷原始request與轉(zhuǎn)換后的request是否為同一個(gè)request,如果不是同一個(gè),則說(shuō)明
// 其是一個(gè)文件請(qǐng)求
multipartRequestParsed = (processedRequest != request);
// 這里getHandler()方法就是通過(guò)遍歷當(dāng)前Spring容器中所有定義的HandlerMapping對(duì)象,
// 通過(guò)調(diào)用它們的getHandler()方法,看當(dāng)前的HandlerMapping能否將當(dāng)前request映射
// 到某個(gè)handler,也就是某個(gè)Controller方法上,如果能夠映射到,則說(shuō)明該handler能夠
// 處理當(dāng)前請(qǐng)求
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 如果每個(gè)HandlerMapping都無(wú)法找到與當(dāng)前request匹配的handler,那么就認(rèn)為
// 無(wú)法處理當(dāng)前請(qǐng)求,此時(shí)一般會(huì)返回給頁(yè)面404狀態(tài)碼
noHandlerFound(processedRequest, response);
return;
}
// 通過(guò)找到的handler,然后在當(dāng)前Spring容器中找到能夠支持將當(dāng)前request請(qǐng)求適配到
// 找到的handler上的HandlerAdapter。這里需要找到這樣的適配器的原因是,我們的handler
// 一般都是Controller的某個(gè)方法,其是一個(gè)Java方法,而當(dāng)前request則是一種符合http
// 協(xié)議的請(qǐng)求,這里是無(wú)法直接將request直接應(yīng)用到handler上的,因而需要使用一個(gè)適配器,
// 也就是這里的HandlerAdapter。由于前面獲取handler的時(shí)候,不同的HandlerMapping
// 所產(chǎn)生的handler是不一樣的,比如ReqeustMappingHandlerMapping產(chǎn)生的handler是一個(gè)
// HandlerMethod對(duì)象,因而這里在判斷某個(gè)HandlerAdapter是否能夠用于適配當(dāng)前handler的
// 時(shí)候是通過(guò)其supports()方法進(jìn)行的,比如RequestMappingHandlerAdapter就是判斷
// 當(dāng)前的handler是否為HandlerMethod類型,從而判斷其是否能夠用于適配當(dāng)前handler。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
// 這里判斷請(qǐng)求方式是否為GET或HEAD請(qǐng)求,如果是這兩種請(qǐng)求的一種,那么就會(huì)判斷
// 當(dāng)前請(qǐng)求的資源是否超過(guò)了其lastModified時(shí)間,如果沒(méi)超過(guò),則直接返回,
// 并且告知瀏覽器可以直接使用緩存來(lái)處理當(dāng)前請(qǐng)求
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;
}
}
// 這里在真正處理請(qǐng)求之前會(huì)獲取容器中所有的攔截器,也就是HandlerInterceptor對(duì)象,
// 然后依次調(diào)用其preHandle()方法,如果某個(gè)preHandle()方法返回了false,那么就說(shuō)明
// 當(dāng)前請(qǐng)求無(wú)法通過(guò)攔截器的過(guò)濾,因而就會(huì)直接出發(fā)其afterCompletion()方法,只有在
// 所有的preHandle()方法都返回true時(shí)才會(huì)認(rèn)為當(dāng)前請(qǐng)求是能夠使用目標(biāo)handler進(jìn)行處理的
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 在當(dāng)前請(qǐng)求通過(guò)了所有攔截器的預(yù)處理之后,這里就直接調(diào)用HandlerAdapter.handle()
// 方法來(lái)處理當(dāng)前請(qǐng)求,并且將處理結(jié)果封裝為一個(gè)ModelAndView對(duì)象。該對(duì)象中主要有兩個(gè)
// 屬性:view和model,這里的view存儲(chǔ)了后續(xù)需要展示的邏輯視圖名或視圖對(duì)象,而model
// 中則保存了用于渲染視圖所需要的屬性
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 如果當(dāng)前是一個(gè)異步任務(wù),那么就會(huì)釋放當(dāng)前線程,等待異步任務(wù)處理完成之后才將
// 任務(wù)的處理結(jié)果返回到頁(yè)面
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 如果返回的ModelAndView對(duì)象中沒(méi)有指定視圖名或視圖對(duì)象,那么就會(huì)根據(jù)當(dāng)前請(qǐng)求的url
// 來(lái)生成一個(gè)視圖名
applyDefaultViewName(processedRequest, mv);
// 在請(qǐng)求處理完成之后,依次調(diào)用攔截器的postHandle()方法,對(duì)請(qǐng)求進(jìn)行后置處理
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
} catch (Throwable err) {
// 將處理請(qǐng)求過(guò)程中產(chǎn)生的異常封裝到dispatchException中
dispatchException = new NestedServletException("Handler dispatch failed",
err);
}
// 這里主要是請(qǐng)求處理之后生成的視圖進(jìn)行渲染,也包括出現(xiàn)異常之后對(duì)異常的處理。
// 渲染完之后會(huì)依次調(diào)用攔截器的afterCompletion()方法來(lái)對(duì)請(qǐng)求進(jìn)行最終處理
processDispatchResult(processedRequest, response, mappedHandler, mv,
dispatchException);
} catch (Exception ex) {
// 如果在上述過(guò)程中任意位置拋出異常,包括渲染視圖時(shí)拋出異常,那么都會(huì)觸發(fā)攔截器的
// afterCompletion()方法的調(diào)用
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
} catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
} finally {
// 如果當(dāng)前異步任務(wù)已經(jīng)開(kāi)始,則觸發(fā)異步任務(wù)攔截器的afterConcurrentHandlingStarted()方法
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest,
response);
}
} else {
// 如果當(dāng)前是一個(gè)文件請(qǐng)求,則清理當(dāng)前request中的文件數(shù)據(jù)
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
這里doDispatch()方法是進(jìn)行請(qǐng)求分發(fā)和處理的主干部分,其主要分為如下幾個(gè)步驟:
- 判斷當(dāng)前是否為文件請(qǐng)求,如果是,則將request對(duì)象類型轉(zhuǎn)換為MultipartHttpServletRequest;
- 在HandlerMapping中查找能夠處理當(dāng)前request的HandlerMapping,并且獲取能夠處理當(dāng)前請(qǐng)求的handler;
- 根據(jù)獲取到的handler,查找當(dāng)前容器中支持將當(dāng)前request適配到該handler的HandlerAdapter;
- 應(yīng)用容器中所有攔截器的preHandle()方法,只有在所有的preHandle()方法都通過(guò)之后才會(huì)將當(dāng)前請(qǐng)求交由具體的handler進(jìn)行處理;
- 調(diào)用HandlerAdapter.handle()方法將request適配給獲取到的handler進(jìn)行處理;
- 應(yīng)用容器中所有攔截器的postHandle()方法,以對(duì)當(dāng)前請(qǐng)求進(jìn)行后置處理;
- 根據(jù)處理后得到的ModelAndView對(duì)象對(duì)視圖進(jìn)行渲染;
- 應(yīng)用容器中所有攔截器的afterCompletion()方法,以對(duì)當(dāng)前請(qǐng)求進(jìn)行完成處理。
2. handler獲取
從前面的步驟可以看出,請(qǐng)求的具體處理過(guò)程主要是通過(guò)HandlerMapping根據(jù)當(dāng)前request獲取到對(duì)應(yīng)的handler,然后交由HandlerAdapter將request適配給該handler進(jìn)行處理,并將處理結(jié)果封裝為一個(gè)ModelAndView對(duì)象,最后將該ModelAndView對(duì)象渲染出來(lái)。這里我們首先看HandlerMapping根據(jù)request查找具體的handler的過(guò)程:
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// 遍歷當(dāng)前容器中所有的HandlerMapping對(duì)象,調(diào)用其getHandler()方法,如果其能夠根據(jù)
// 當(dāng)前request獲取一個(gè)handler,那么就直接返回。
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '"
+ getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
這里的邏輯比較簡(jiǎn)單,就是遍歷當(dāng)前容器中所有的HandlerMapping對(duì)象,然后依次判斷其是否能夠根據(jù)當(dāng)前request獲取一個(gè)handler,如果能夠獲取就直接使用該handler。這里關(guān)于HandlerMapping將request映射為handler的過(guò)程可以閱讀本人之前的文章:Spring MVC之RequestMappingHandlerMapping匹配。
3. HandlerAdapter獲取與請(qǐng)求處理
在獲取到具體的handler之后,Dispatcher就會(huì)根據(jù)獲取到的handler查找能夠?qū)?dāng)前request適配到該handler的Adapter,這里獲取HandlerAdapter的代碼如下:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
// 遍歷當(dāng)前容器中所有的HandlerAdapter,通過(guò)調(diào)用其supports()方法,判斷當(dāng)前HandlerAdapter
// 能否用于適配當(dāng)前的handler,如果可以,則直接使用該HandlerAdapter
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
}
// 如果找不到任何一個(gè)HandlerAdapter用于適配當(dāng)前請(qǐng)求,則拋出異常
throw new ServletException("No adapter for handler [" + handler
+ "]: The DispatcherServlet configuration needs to include a HandlerAdapter"
+ " that supports this handler");
}
這里獲取HandlerAdapter的過(guò)程與HandlerMapping非常的相似,也是遍歷當(dāng)前容器中所有的HandlerAdapter對(duì)象,然后調(diào)用其supports()方法,判斷該適配器能否應(yīng)用于當(dāng)前handler的適配,如果可以則直接使用該HandlerAdapter。關(guān)于HandlerAdapter進(jìn)行request與handler適配的過(guò)程,讀者可閱讀本人之前的文章:Spring MVC之RequestMappingHandlerAdapter詳解。
4. 視圖渲染
在HandlerAdapter進(jìn)行了請(qǐng)求的適配,并且調(diào)用了目標(biāo)handler之后,其會(huì)返回一個(gè)ModelAndView對(duì)象,該對(duì)象中保存有用于渲染視圖的模型數(shù)據(jù)和需要渲染的視圖名。具體的視圖渲染工作是在processDispatchResult()方法中進(jìn)行的,這里我們直接閱讀器源碼:
private void processDispatchResult(HttpServletRequest request,
HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler,
@Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
// 用于標(biāo)記當(dāng)前生成view是否是異常處理之后生成的view
boolean errorView = false;
if (exception != null) {
// 如果當(dāng)前的異常是ModelAndViewDefiningException類型,則說(shuō)明是ModelAndView的定義
// 異常,那么就會(huì)調(diào)用其getModelAndView()方法生成一個(gè)新的view
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
} else {
// 如果生成的異常是其他類型的異常,就會(huì)在當(dāng)前容器中查找能夠處理當(dāng)前異常的“攔截器”,
// 找到之后調(diào)用這些攔截器,然后生成一個(gè)新的ModelAndView
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// 如果得到的ModelAndView對(duì)象(無(wú)論是否為異常處理之后生成的ModelAndView)不為空,并且沒(méi)有被清理,
// 那么就會(huì)對(duì)其進(jìn)行渲染,渲染的主要邏輯在render()方法中
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
// 如果當(dāng)前是異常處理之后生成的視圖,那么就請(qǐng)求當(dāng)前request中與異常相關(guān)的屬性
WebUtils.clearErrorRequestAttributes(request);
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '"
+ getServletName() + "': assuming HandlerAdapter completed request "
+ "handling");
}
}
// 如果當(dāng)前正在進(jìn)行異步請(qǐng)求任務(wù)的調(diào)用,則直接釋放當(dāng)前線程,等異步任務(wù)處理完之后再進(jìn)行處理
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
return;
}
// 在視圖渲染完成之后,依次調(diào)用當(dāng)前容器中所有攔截器的afterCompletion()方法
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
從上面的邏輯可以看出,在進(jìn)行視圖渲染時(shí),首先會(huì)判斷請(qǐng)求處理過(guò)程中是否拋出了異常,如果拋出了異常,則會(huì)調(diào)用相應(yīng)的異常處理器,獲取異常處理之后的ModelAndView對(duì)象,然后通過(guò)ModelAndView對(duì)象渲染具體的視圖,最后會(huì)依次觸發(fā)當(dāng)前容器中所有攔截器的afterCompletion()方法。這里對(duì)視圖的具體渲染工作在render()方法中,我們繼續(xù)閱讀其源碼:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 獲取當(dāng)前請(qǐng)求的Locale信息,該信息在進(jìn)行視圖的國(guó)際化展示時(shí)將會(huì)非常有用
Locale locale = (this.localeResolver != null
? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 如果視圖名不為空,那么就會(huì)使用當(dāng)前容器中配置的ViewResolver根據(jù)視圖名獲取一個(gè)View對(duì)象
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '"
+ mv.getViewName() + "' in servlet with name '" + getServletName() + "'");
}
} else {
// 如果ModelAndView中沒(méi)有視圖名,而提供的View對(duì)象,則直接使用該View對(duì)象
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a "
+ "view name nor a View object in servlet with name '"
+ getServletName() + "'");
}
}
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '"
+ getServletName() + "'");
}
try {
// 設(shè)置響應(yīng)的status屬性
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// 調(diào)用View對(duì)象的render()方法來(lái)渲染具體的視圖
view.render(mv.getModelInternal(), request, response);
} catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet"
+ " with name '" + getServletName() + "'", ex);
}
throw ex;
}
}
這里的render()方法才是進(jìn)行視圖渲染的真正方法,首先該方法首先通過(guò)ModelAndView對(duì)象獲取所要渲染的視圖名,通過(guò)ViewResolver生成一個(gè)用于視圖渲染的View對(duì)象;如果ModelAndView中不是保存的視圖名,而是保存的View對(duì)象,則直接使用該對(duì)象。在生成View對(duì)象之后,通過(guò)調(diào)用該對(duì)象的render()方法渲染得到具體的視圖。這里關(guān)于ViewResolver如何獲取到View對(duì)象,并且如何進(jìn)行視圖渲染的過(guò)程,讀者可以閱讀本人的文章:Spring MVC之視圖解析。
5. 小結(jié)
本文首先從整體上講解了DispatcherServlet是如何對(duì)請(qǐng)求進(jìn)行聚合并且處理的,然后分別從handler獲取,HandlerAdapter進(jìn)行請(qǐng)求適配,以及視圖的渲染三個(gè)方面對(duì)請(qǐng)求處理的整體流程進(jìn)行了講解。這里主要是對(duì)DispatcherServlet處理請(qǐng)求的整體流程進(jìn)行講解,其各個(gè)部分的細(xì)節(jié)讀者可以閱讀本人前面的文章以進(jìn)行詳細(xì)的了解。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Java調(diào)用構(gòu)造函數(shù)和方法及使用詳解
在Java編程中,構(gòu)造函數(shù)用于初始化新創(chuàng)建的對(duì)象,而方法則用于執(zhí)行對(duì)象的行為,構(gòu)造函數(shù)在使用new關(guān)鍵字創(chuàng)建類實(shí)例時(shí)自動(dòng)調(diào)用,沒(méi)有返回類型,并且名稱與類名相同,本文通過(guò)示例詳細(xì)介紹了如何在Java中使用構(gòu)造函數(shù)和方法,感興趣的朋友一起看看吧2024-10-10
SpringBoot項(xiàng)目@Async方法問(wèn)題解決方案
這篇文章主要介紹了SpringBoot項(xiàng)目@Async方法問(wèn)題解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
使用springboot不自動(dòng)初始化數(shù)據(jù)庫(kù)連接池
這篇文章主要介紹了使用springboot不自動(dòng)初始化數(shù)據(jù)庫(kù)連接池,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
Java編程中的防轉(zhuǎn)義和轉(zhuǎn)義技巧匯總
在編程過(guò)程中,我們常常需要處理特殊字符和特定上下文,以確保生成的內(nèi)容在正確的環(huán)境中能夠被解析和顯示,本文將介紹一些常見(jiàn)的防轉(zhuǎn)義或者轉(zhuǎn)義處理的編程技巧,需要的可以參考一下2023-07-07
解決jpa查詢語(yǔ)句自動(dòng)變成了update的問(wèn)題
這篇文章主要介紹了解決jpa查詢語(yǔ)句自動(dòng)變成了update的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
MybatisPlus中如何調(diào)用Oracle存儲(chǔ)過(guò)程
這篇文章主要介紹了MybatisPlus中如何調(diào)用Oracle存儲(chǔ)過(guò)程的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05
詳解springboot解決第三方依賴jar包的問(wèn)題
本篇文章主要介紹了詳解springboot解決第三方依賴jar包的問(wèn)題,解決了第三方依賴jar包的問(wèn)題,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09

