最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring MVC學(xué)習(xí)之DispatcherServlet請求處理詳析

 更新時間:2018年11月18日 10:37:24   作者:愛寶貝丶  
這篇文章主要給大家介紹了關(guān)于Spring MVC學(xué)習(xí)教程之DispatcherServlet請求處理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧

前言

要深入理解spring mvc的工作流程,就需要先了解spring mvc的架構(gòu):

從上圖可以看到 前端控制器DispatcherServlet在其中起著主導(dǎo)作用,理解了DispatcherServlet 就完全可以說弄清楚了spring mvc。

DispatcherServlet作為Spring用于處理web請求注冊的唯一一個Servlet,所有的請求都是經(jīng)由DispatcherServlet進(jìn)行分發(fā)處理的。本文主要講解DispatcherServlet是如何對請求進(jìn)行分發(fā),處理,并且生成相應(yīng)的視圖的。

1. 整體結(jié)構(gòu)

在HttpServlet中,其對不同方式的請求進(jìn)行了分發(fā),比如對于GET請求,其提供了doGet()方法,對于POST請求,其提供了doPost()方法等等。通過這種方式,子類可以針對于當(dāng)前請求的方式實現(xiàn)不同的方法即可。但是在DispatcherServlet中,由于需要使用同一的方式對不同的請求進(jìn)行處理,因而其對各個請求方式進(jìn)行了整合,如下就是DispatcherServlet針對GET和POST請求所編寫的同一處理邏輯:

@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);
}

可以看到,無論是GET請求還是POST請求,DispatcherServlet都是委托給了processRequest()方法處理,對于其他的請求方式,其處理方式也是類似的。通過這種方式,DispatcherServlet將各個請求整合在了一起,雖然整合在了一起,但是request中也還是保存有當(dāng)前請求的請求方式的,因而保存了后續(xù)對請求進(jìn)行分發(fā)的能力。這里我們直接看processRequest()方法是如何處理各個請求的:

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 long startTime = System.currentTimeMillis();
 Throwable failureCause = null;
 // 獲取先前請求的LocaleContext
 LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
 // 獲取當(dāng)前請求的LocaleContext,其中保存了當(dāng)前請求的Locale信息
 LocaleContext localeContext = buildLocaleContext(request);

 // 獲取先前請求的Attributes信息
 RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
 // 獲取當(dāng)前請求的Attributes信息,其中保存了當(dāng)前請求的各個屬性數(shù)據(jù)
 ServletRequestAttributes requestAttributes = 
 buildRequestAttributes(request, response, previousAttributes);

 // 獲取當(dāng)前請求的WebAsyncManager,這只有在當(dāng)前請求是請求的異步任務(wù)時才會真正用到
 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
 // 注冊異步任務(wù)的攔截器,如果請求的是異步任務(wù),這個攔截器可以攔截異步任務(wù)的前置,后置和異常等情況
 asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), 
 new RequestBindingInterceptor());

 // 將當(dāng)前請求的Locale,Attributes信息初始化到對應(yīng)的ThreadLocal對象中,用于后續(xù)使用
 initContextHolders(request, localeContext, requestAttributes);

 try {
 // 對當(dā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 {
 // 在請求完成之后,判斷當(dāng)前請求的Locale和Attributes信息是否需要繼承,如果需要繼承,
 // 則會將Locale信息設(shè)置到inheritableLocaleContextHolder中,而將Attributes
 // 信息設(shè)置到inheritableRequestAttributesHolder中;否則就會移除對應(yīng)的信息,
 // 而只為當(dāng)前請求的ContextHolder設(shè)置相應(yīng)的屬性
 resetContextHolders(request, previousLocaleContext, previousAttributes);
 if (requestAttributes != null) {
 // 調(diào)用已注冊的在當(dāng)前請求被銷毀時的回調(diào)函數(shù),并且更新Session中當(dā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ā)布請求已經(jīng)完成的事件,以便對該事件進(jìn)行監(jiān)聽的程序進(jìn)行相應(yīng)的處理
 publishRequestHandledEvent(request, response, startTime, failureCause);
 }
}

可以看到,processRequest()方法主要是對Locale和Attributes信息進(jìn)行了處理,然后就通過doService()方法對請求再次進(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)前請求是否為include請求,如果是include請求,那么就會將當(dāng)前請求中的
 // 數(shù)據(jù)都放入一個快照中,在當(dāng)前請求完成之后,會從該塊中中取出數(shù)據(jù),然后將其重新加載到
 // 當(dāng)前request中,以便request進(jìn)行后續(xù)的處理。這里默認(rèn)情況下是會對所有的屬性進(jìn)行處理的,
 // 因為cleanupAfterInclude默認(rèn)值為true,如果將其設(shè)置為false,那么就只會對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)請求如果是重定向的請求,那么可以將一些屬性保存在FlashMap
 // 中,然后通過FlashMapManager進(jìn)行管理,從而在重定向之后能夠獲取到重定向之前所保存的請求
 if (this.flashMapManager != null) {
 // 在當(dāng)前請求中獲取FlashMap數(shù)據(jù),如果不是重定向之后的請求,那么這里獲取到的就是空值
 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 {
 // 這里才是真正的對請求進(jìn)行分發(fā)處理的位置
 doDispatch(request, response);
 } finally {
 // 判斷當(dāng)前請求不是一個異步任務(wù)的請求,但是是一個include請求,那么就會重新加載
 // 請求之前保存的快照數(shù)據(jù)
 if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
 if (attributesSnapshot != null) {
 restoreAttributesAfterInclude(request, attributesSnapshot);
 }
 }
 }
}

這里的doService()方法也還沒有對請求進(jìn)行真正的處理,其首先判斷了當(dāng)前請求是不是一個include請求,如果是include請求,那么就將請求的屬性都保存在一個快照中,以便請求完成之后能夠重新進(jìn)行加載;然后會判斷當(dāng)前是否是一個重定向之后的請求,如果是重定向之后的請求,那么其FlashMapManager就不是空的,此時會將重定向之前保存的屬性重新加載到當(dāng)前請求中;最后doService()方法才會調(diào)用doDispatch()方法進(jìn)行請求的分發(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)前請求是否為一個文件請求,這里的判斷方式就是要求當(dāng)前請求滿足兩點:①請求
 // 方式是POST;②判斷contentType是否以multipart/開頭。如果滿足這兩點,那么就認(rèn)為當(dāng)前
 // 請求是一個文件請求,此時會將當(dāng)前請求的request對象封裝為一個
 // MultipartHttpServletRequest對象,這也是我們在定義文件請求的Controller時
 // 能夠?qū)equest參數(shù)寫為MultipartHttpServletRequest的原因。這里如果不是文件請求,
 // 那么會將request直接返回。
 processedRequest = checkMultipart(request);
 // 這里判斷原始request與轉(zhuǎn)換后的request是否為同一個request,如果不是同一個,則說明
 // 其是一個文件請求
 multipartRequestParsed = (processedRequest != request);
 // 這里getHandler()方法就是通過遍歷當(dāng)前Spring容器中所有定義的HandlerMapping對象,
 // 通過調(diào)用它們的getHandler()方法,看當(dāng)前的HandlerMapping能否將當(dāng)前request映射
 // 到某個handler,也就是某個Controller方法上,如果能夠映射到,則說明該handler能夠
 // 處理當(dāng)前請求
 mappedHandler = getHandler(processedRequest);
 if (mappedHandler == null) {
 // 如果每個HandlerMapping都無法找到與當(dāng)前request匹配的handler,那么就認(rèn)為
 // 無法處理當(dāng)前請求,此時一般會返回給頁面404狀態(tài)碼
 noHandlerFound(processedRequest, response);
 return;
 }

 // 通過找到的handler,然后在當(dāng)前Spring容器中找到能夠支持將當(dāng)前request請求適配到
 // 找到的handler上的HandlerAdapter。這里需要找到這樣的適配器的原因是,我們的handler
 // 一般都是Controller的某個方法,其是一個Java方法,而當(dāng)前request則是一種符合http
 // 協(xié)議的請求,這里是無法直接將request直接應(yīng)用到handler上的,因而需要使用一個適配器,
 // 也就是這里的HandlerAdapter。由于前面獲取handler的時候,不同的HandlerMapping
 // 所產(chǎn)生的handler是不一樣的,比如ReqeustMappingHandlerMapping產(chǎn)生的handler是一個
 // HandlerMethod對象,因而這里在判斷某個HandlerAdapter是否能夠用于適配當(dāng)前handler的
 // 時候是通過其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);
 // 這里判斷請求方式是否為GET或HEAD請求,如果是這兩種請求的一種,那么就會判斷
 // 當(dāng)前請求的資源是否超過了其lastModified時間,如果沒超過,則直接返回,
 // 并且告知瀏覽器可以直接使用緩存來處理當(dā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;
 }
 }

 // 這里在真正處理請求之前會獲取容器中所有的攔截器,也就是HandlerInterceptor對象,
 // 然后依次調(diào)用其preHandle()方法,如果某個preHandle()方法返回了false,那么就說明
 // 當(dāng)前請求無法通過攔截器的過濾,因而就會直接出發(fā)其afterCompletion()方法,只有在
 // 所有的preHandle()方法都返回true時才會認(rèn)為當(dāng)前請求是能夠使用目標(biāo)handler進(jìn)行處理的
 if (!mappedHandler.applyPreHandle(processedRequest, response)) {
 return;
 }

 // 在當(dāng)前請求通過了所有攔截器的預(yù)處理之后,這里就直接調(diào)用HandlerAdapter.handle()
 // 方法來處理當(dāng)前請求,并且將處理結(jié)果封裝為一個ModelAndView對象。該對象中主要有兩個
 // 屬性:view和model,這里的view存儲了后續(xù)需要展示的邏輯視圖名或視圖對象,而model
 // 中則保存了用于渲染視圖所需要的屬性
 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

 // 如果當(dāng)前是一個異步任務(wù),那么就會釋放當(dāng)前線程,等待異步任務(wù)處理完成之后才將
 // 任務(wù)的處理結(jié)果返回到頁面
 if (asyncManager.isConcurrentHandlingStarted()) {
 return;
 }

 // 如果返回的ModelAndView對象中沒有指定視圖名或視圖對象,那么就會根據(jù)當(dāng)前請求的url
 // 來生成一個視圖名
 applyDefaultViewName(processedRequest, mv);
 // 在請求處理完成之后,依次調(diào)用攔截器的postHandle()方法,對請求進(jìn)行后置處理
 mappedHandler.applyPostHandle(processedRequest, response, mv);
 } catch (Exception ex) {
 dispatchException = ex;
 } catch (Throwable err) {
 // 將處理請求過程中產(chǎn)生的異常封裝到dispatchException中
 dispatchException = new NestedServletException("Handler dispatch failed", 
 err);
 }
 
 // 這里主要是請求處理之后生成的視圖進(jìn)行渲染,也包括出現(xiàn)異常之后對異常的處理。
 // 渲染完之后會依次調(diào)用攔截器的afterCompletion()方法來對請求進(jìn)行最終處理
 processDispatchResult(processedRequest, response, mappedHandler, mv, 
 dispatchException);
 } catch (Exception ex) {
 // 如果在上述過程中任意位置拋出異常,包括渲染視圖時拋出異常,那么都會觸發(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)開始,則觸發(fā)異步任務(wù)攔截器的afterConcurrentHandlingStarted()方法
 if (asyncManager.isConcurrentHandlingStarted()) {
 if (mappedHandler != null) {
 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, 
 response);
 }
 } else {
 // 如果當(dāng)前是一個文件請求,則清理當(dāng)前request中的文件數(shù)據(jù)
 if (multipartRequestParsed) {
 cleanupMultipart(processedRequest);
 }
 }
 }
}

這里doDispatch()方法是進(jìn)行請求分發(fā)和處理的主干部分,其主要分為如下幾個步驟:

  • 判斷當(dāng)前是否為文件請求,如果是,則將request對象類型轉(zhuǎn)換為MultipartHttpServletRequest;
  • 在HandlerMapping中查找能夠處理當(dāng)前request的HandlerMapping,并且獲取能夠處理當(dāng)前請求的handler;
  • 根據(jù)獲取到的handler,查找當(dāng)前容器中支持將當(dāng)前request適配到該handler的HandlerAdapter;
  • 應(yīng)用容器中所有攔截器的preHandle()方法,只有在所有的preHandle()方法都通過之后才會將當(dāng)前請求交由具體的handler進(jìn)行處理;
  • 調(diào)用HandlerAdapter.handle()方法將request適配給獲取到的handler進(jìn)行處理;
  • 應(yīng)用容器中所有攔截器的postHandle()方法,以對當(dāng)前請求進(jìn)行后置處理;
  • 根據(jù)處理后得到的ModelAndView對象對視圖進(jìn)行渲染;
  • 應(yīng)用容器中所有攔截器的afterCompletion()方法,以對當(dāng)前請求進(jìn)行完成處理。

2. handler獲取

從前面的步驟可以看出,請求的具體處理過程主要是通過HandlerMapping根據(jù)當(dāng)前request獲取到對應(yīng)的handler,然后交由HandlerAdapter將request適配給該handler進(jìn)行處理,并將處理結(jié)果封裝為一個ModelAndView對象,最后將該ModelAndView對象渲染出來。這里我們首先看HandlerMapping根據(jù)request查找具體的handler的過程:

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
 if (this.handlerMappings != null) {
 // 遍歷當(dāng)前容器中所有的HandlerMapping對象,調(diào)用其getHandler()方法,如果其能夠根據(jù)
 // 當(dāng)前request獲取一個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;
}

這里的邏輯比較簡單,就是遍歷當(dāng)前容器中所有的HandlerMapping對象,然后依次判斷其是否能夠根據(jù)當(dāng)前request獲取一個handler,如果能夠獲取就直接使用該handler。這里關(guān)于HandlerMapping將request映射為handler的過程可以閱讀本人之前的文章:Spring MVC之RequestMappingHandlerMapping匹配。

3. HandlerAdapter獲取與請求處理

在獲取到具體的handler之后,Dispatcher就會根據(jù)獲取到的handler查找能夠?qū)?dāng)前request適配到該handler的Adapter,這里獲取HandlerAdapter的代碼如下:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
 if (this.handlerAdapters != null) {
 // 遍歷當(dāng)前容器中所有的HandlerAdapter,通過調(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;
 }
 }
 }
 
 // 如果找不到任何一個HandlerAdapter用于適配當(dāng)前請求,則拋出異常
 throw new ServletException("No adapter for handler [" + handler 
 + "]: The DispatcherServlet configuration needs to include a HandlerAdapter" 
 + " that supports this handler");
}

這里獲取HandlerAdapter的過程與HandlerMapping非常的相似,也是遍歷當(dāng)前容器中所有的HandlerAdapter對象,然后調(diào)用其supports()方法,判斷該適配器能否應(yīng)用于當(dāng)前handler的適配,如果可以則直接使用該HandlerAdapter。關(guān)于HandlerAdapter進(jìn)行request與handler適配的過程,讀者可閱讀本人之前的文章:Spring MVC之RequestMappingHandlerAdapter詳解。

4. 視圖渲染

在HandlerAdapter進(jìn)行了請求的適配,并且調(diào)用了目標(biāo)handler之后,其會返回一個ModelAndView對象,該對象中保存有用于渲染視圖的模型數(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類型,則說明是ModelAndView的定義
 // 異常,那么就會調(diào)用其getModelAndView()方法生成一個新的view
 if (exception instanceof ModelAndViewDefiningException) {
 logger.debug("ModelAndViewDefiningException encountered", exception);
 mv = ((ModelAndViewDefiningException) exception).getModelAndView();
 } else {
 // 如果生成的異常是其他類型的異常,就會在當(dāng)前容器中查找能夠處理當(dāng)前異常的“攔截器”,
 // 找到之后調(diào)用這些攔截器,然后生成一個新的ModelAndView
 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
 mv = processHandlerException(request, response, handler, exception);
 errorView = (mv != null);
 }
 }

 // 如果得到的ModelAndView對象(無論是否為異常處理之后生成的ModelAndView)不為空,并且沒有被清理,
 // 那么就會對其進(jìn)行渲染,渲染的主要邏輯在render()方法中
 if (mv != null && !mv.wasCleared()) {
 render(mv, request, response);
 if (errorView) {
 // 如果當(dā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)行異步請求任務(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)行視圖渲染時,首先會判斷請求處理過程中是否拋出了異常,如果拋出了異常,則會調(diào)用相應(yīng)的異常處理器,獲取異常處理之后的ModelAndView對象,然后通過ModelAndView對象渲染具體的視圖,最后會依次觸發(fā)當(dāng)前容器中所有攔截器的afterCompletion()方法。這里對視圖的具體渲染工作在render()方法中,我們繼續(xù)閱讀其源碼:

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
 // 獲取當(dāng)前請求的Locale信息,該信息在進(jìn)行視圖的國際化展示時將會非常有用
 Locale locale = (this.localeResolver != null 
 ? this.localeResolver.resolveLocale(request) : request.getLocale());
 response.setLocale(locale);

 View view;
 String viewName = mv.getViewName();
 if (viewName != null) {
 // 如果視圖名不為空,那么就會使用當(dāng)前容器中配置的ViewResolver根據(jù)視圖名獲取一個View對象
 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中沒有視圖名,而提供的View對象,則直接使用該View對象
 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對象的render()方法來渲染具體的視圖
 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)行視圖渲染的真正方法,首先該方法首先通過ModelAndView對象獲取所要渲染的視圖名,通過ViewResolver生成一個用于視圖渲染的View對象;如果ModelAndView中不是保存的視圖名,而是保存的View對象,則直接使用該對象。在生成View對象之后,通過調(diào)用該對象的render()方法渲染得到具體的視圖。這里關(guān)于ViewResolver如何獲取到View對象,并且如何進(jìn)行視圖渲染的過程,讀者可以閱讀本人的文章:Spring MVC之視圖解析。

5. 小結(jié)

本文首先從整體上講解了DispatcherServlet是如何對請求進(jìn)行聚合并且處理的,然后分別從handler獲取,HandlerAdapter進(jìn)行請求適配,以及視圖的渲染三個方面對請求處理的整體流程進(jìn)行了講解。這里主要是對DispatcherServlet處理請求的整體流程進(jìn)行講解,其各個部分的細(xì)節(jié)讀者可以閱讀本人前面的文章以進(jìn)行詳細(xì)的了解。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • java對象拷貝常見面試題及應(yīng)答匯總

    java對象拷貝常見面試題及應(yīng)答匯總

    在本篇文章里小編給大家整理的是關(guān)于java對象拷貝常見面試題的相關(guān)內(nèi)容,需要的朋友們可以學(xué)習(xí)下。
    2020-02-02
  • idea遠(yuǎn)程Debug部署在服務(wù)器上的服務(wù)

    idea遠(yuǎn)程Debug部署在服務(wù)器上的服務(wù)

    在開發(fā)的時候我們通常在本地代碼上debug程序,但是服務(wù)部署到了開發(fā)環(huán)境服務(wù)器上,如何遠(yuǎn)程調(diào)試,本文主要介紹了idea遠(yuǎn)程Debug部署在服務(wù)器上的服務(wù),具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • IDEA中如何引入spring的命名空間

    IDEA中如何引入spring的命名空間

    這篇文章主要介紹了IDEA中如何引入spring的命名空間問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • java 發(fā)送http和https請求的實例

    java 發(fā)送http和https請求的實例

    下面小編就為大家分享一篇java 發(fā)送http和https請求的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-01-01
  • 解決idea check out 切換分支時找不到需要的分支問題

    解決idea check out 切換分支時找不到需要的分支問題

    這篇文章主要介紹了解決idea check out 切換分支時找不到需要的分支問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • 一篇文章帶你了解JVM內(nèi)存模型

    一篇文章帶你了解JVM內(nèi)存模型

    本文講解了Java 內(nèi)存模型來看看解決可見性、有序性問題的 Java 內(nèi)存模型(JMM),今天通過本文給大家介紹Java 內(nèi)存模型(JVM)的相關(guān)知識,感興趣的朋友一起看看吧
    2021-09-09
  • xml與Java對象的轉(zhuǎn)換詳解

    xml與Java對象的轉(zhuǎn)換詳解

    這篇文章主要介紹了xml與Java對象的轉(zhuǎn)換詳解的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • java實現(xiàn)String類型和Date類型相互轉(zhuǎn)換

    java實現(xiàn)String類型和Date類型相互轉(zhuǎn)換

    很多人表示,java將string類型轉(zhuǎn)為date類型不知道應(yīng)該怎樣做,本文就來介紹一下java實現(xiàn)String類型和Date類型相互轉(zhuǎn)換,具有一定的參考價值,感興趣的可以了解一下
    2023-10-10
  • Java實現(xiàn)HashMap排序方法的示例詳解

    Java實現(xiàn)HashMap排序方法的示例詳解

    這篇文章主要通過一些示例為大家介紹了Java對HashMap進(jìn)行排序的方法,幫助大家更好的理解和使用Java,感興趣的朋友可以了解一下
    2022-05-05
  • 使用jekins自動構(gòu)建部署java maven項目的方法步驟

    使用jekins自動構(gòu)建部署java maven項目的方法步驟

    這篇文章主要介紹了使用jekins自動構(gòu)建部署java maven項目的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01

最新評論

永清县| 昔阳县| 海门市| 隆林| 济阳县| 长乐市| 巍山| 陆河县| 盐亭县| 梁平县| 炉霍县| 白朗县| 修文县| 鄢陵县| 东台市| 虹口区| 厦门市| 太原市| 盐津县| 梓潼县| 新建县| 赤峰市| 鄱阳县| 龙山县| 兴海县| 敖汉旗| 洛浦县| 金沙县| 汾阳市| 吉木萨尔县| 曲松县| 盐山县| 长阳| 榆社县| 深州市| 轮台县| 雅安市| 凤阳县| 上林县| 平潭县| 岳阳市|