你要知道的SpringMVC DispatcherServlet執(zhí)行流程及源碼分析都在這里
轉載請注明出處 http://www.itdecent.cn/p/0f981efdfbbd
本系列文章主要根據(jù)源碼講解SpringMVC的啟動過程,以及相關重要組件的源碼分析。閱讀此系列文章需要具備Spring以及SpringMVC相關知識。本文將分以下幾篇文章進行講解,讀者可按需查閱。
- SpringMVC 啟動流程及相關源碼分析
- SpringMVC DispatcherServlet執(zhí)行流程及源碼分析
- SpringMVC HandlerMapping源碼分析
- SpringMVC HandlerAdapter源碼分析
DispatcherServlet執(zhí)行流程及相關源碼分析
在前一篇文章SpringMVC 啟動流程及相關源碼分析中,詳細探討了Spring MVC在Web容器中部署后的啟動過程,以及相關源碼分析,同時也討論了DispatcherServlet類的初始化創(chuàng)建過程,相關內(nèi)容在此不再贅述,如有需求可查閱。
本文主要講解DispatcherServlet類獲取用戶請求到響應的全過程,并針對相關源碼進行分析。對于基本的MVC架構本文不再進行講解,有需要的讀者可自行查閱。
首先,讓我們站在Spring MVC的四大組件:DispatcherServlet、HandlerMapping、HandlerAdapter以及ViewResolver的角度來看一下Spring MVC對用戶請求的處理過程,有如下時序圖:

具體處理過程如下:
- 1、用戶請求發(fā)送至
DispatcherServlet類進行處理。 - 2、
DispatcherServlet類遍歷所有配置的HandlerMapping類請求查找Handler。 - 3、
HandlerMapping類根據(jù)request請求的URL等信息查找能夠進行處理的Handler,以及相關攔截器interceptor并構造HandlerExecutionChain。 - 4、
HandlerMapping類將構造的HandlerExecutionChain類的對象返回給前端控制器DispatcherServlet類。 - 5、前端控制器拿著上一步的
Handler遍歷所有配置的HandlerAdapter類請求執(zhí)行Handler。 - 6、
HandlerAdapter類執(zhí)行相關Handler并獲取ModelAndView類的對象。 - 7、
HandlerAdapter類將上一步Handler執(zhí)行結果的ModelAndView 類的對象返回給前端控制器。 - 8、
DispatcherServlet類遍歷所有配置的ViewResolver類請求進行視圖解析。 - 9、
ViewResolver類進行視圖解析并獲取View對象。 - 10、
ViewResolver類向前端控制器返回上一步驟的View對象。 - 11、
DispatcherServlet類進行視圖View的渲染,填充Model。 - 12、
DispatcherServlet類向用戶返回響應。
通過時序圖和上面的講解不難發(fā)現(xiàn),整個Spring MVC對于用戶請求的響應和處理都是以DispatcherServlet類為核心,其他三大組件均與前端控制器進行交互,三大組件之間沒有交互并且互相解耦,因此,三大組件可以替換不同的實現(xiàn)而互相沒有任何影響,提高了整個架構的穩(wěn)定性并且降低了耦合度。接下來會按照上述的響應過程逐一進行講解。
DispatcherServlet類本質(zhì)上依舊是一個Servlet并且其父類實現(xiàn)了Servlet接口,我們知道,Servlet執(zhí)行Service()方法對用戶請求進行響應,根據(jù)前一篇文章的分析方法可以得到人如下的調(diào)用邏輯圖:

從上圖的源碼調(diào)用邏輯可以看出,HttpServlet抽象類實現(xiàn)了Servlet接口的service(ServletRequest, ServletResponse)的方法,因此,用戶請求的第一執(zhí)行方法為該方法,該方法緊接著直接調(diào)用了service(HttpServletRequest, HttpServletResponse)方法,其子類FrameworkServlet抽象類重寫了該方法,因為多態(tài)的特性最終是調(diào)用了FrameworkServlet抽象類的service(HttpServletRequest, HttpServletResponse)方法,FrameworkServlet抽象類同樣也重寫了doHead()、doPost()、doPut()、doDelete()、doOptions()、doTrace()方法,service(ServletRequest, ServletResponse)方法根據(jù)請求類型的不同分別調(diào)用上述方法,上述六個方法都調(diào)用了processRequest()方法,而該方法最終調(diào)用了DispatcherServlet類的doService()方法。通過層層分析,我們找到了最終要調(diào)用的處理用戶請求的方法,doService()之前的方法調(diào)用都比較簡單,這里不再逐一來查看源碼,有興趣的讀者可以自行查閱。
查看doService()的源碼如下:
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@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) + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
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));
}
}
}
// Make framework objects available to handlers and view objects.
/*
將當前Servlet的子IoC容器放入request請求中
由此,我們可以訪問到當前IoC子容器以及根IoC容器中的Bean
*/
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());
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 {
//真正進行用戶請求的處理
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
doService()方法主要進行一些參數(shù)的設置,并將部分參數(shù)放入request請求中,真正執(zhí)行用戶請求并作出響應的方法則為doDispatch()方法,查看doDispatch()方法的源碼如下:
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//用戶的request請求
HttpServletRequest processedRequest = request;
//HandlerExecutionChain局部變量
HandlerExecutionChain mappedHandler = null;
//判斷是否解析了文件類型的數(shù)據(jù),如果有最終需要清理
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
//ModelAndView局部變量
ModelAndView mv = null;
//處理異常局部變量
Exception dispatchException = null;
try {
//檢查是否包含文件等類型的數(shù)據(jù)
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//向HandlerMapping請求查找HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
//如果HandlerExecutionChain為null,則沒有能夠進行處理的Handler,拋出異常
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//根據(jù)查找到的Handler請求查找能夠進行處理的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
//判斷自上次請求后是否有修改,沒有修改直接返回響應
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;
}
}
/*
按順序依次執(zhí)行HandlerInterceptor的preHandle方法
如果任一HandlerInterceptor的preHandle方法沒有通過則不繼續(xù)進行處理
*/
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//通過HandlerAdapter執(zhí)行查找到的handler
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//逆序執(zhí)行HandlerInterceptor的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//渲染視圖填充Model,如果有異常渲染異常頁面
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//如果有異常按倒序執(zhí)行所有HandlerInterceptor的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
//如果有異常按倒序執(zhí)行所有HandlerInterceptor的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
//倒序執(zhí)行所有HandlerInterceptor的afterCompletion方法
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
//如果請求包含文件類型的數(shù)據(jù)則進行相關清理工作
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
根據(jù)上述源碼并結合文章開始講解的DispatcherServlet類結合三大組件對用戶請求的處理過程不難理解相關處理流程。
doDispatch()方法通過調(diào)用getHandler()方法并傳入reuqest通過HandlerMapping查找HandlerExecutionChain,查看其源碼如下:
/**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
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;
}
getHandler()方法遍歷了開發(fā)者配置的所有HandlerMapping類根據(jù)request請求來查找HandlerExecutionChain,從這里可以看出,Spring MVC是支持用戶配置多個HandlerMapping類的,在處理用戶請求時會逐一查找,找到后立即返回,因此,如果多個HandlerMapping類都能夠處理同一request請求,只會返回第一個能夠處理的HandlerMapping類構造的HandlerExecutionChain,所以在配置HandlerMapping類時需要注意不要對同一請求多次進行處理,由于篇幅問題HandlerMapping類如何具體查找Handler并構造HandlerExecutionChain的細節(jié)不在此進行講解,如有興趣可以查閱本系列文章的第三篇SpringMVC HandlerMapping源碼分析。
如果沒有找到對應的HandlerExecutionChain對象,則會執(zhí)行noHandlerFound()方法,繼續(xù)查看其源碼如下:
/**
* No handler found -> set appropriate HTTP response status.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception if preparing the response failed
*/
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (pageNotFoundLogger.isWarnEnabled()) {
pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + getRequestUri(request) +
"] in DispatcherServlet with name '" + getServletName() + "'");
}
if (this.throwExceptionIfNoHandlerFound) {
throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
new ServletServerHttpRequest(request).getHeaders());
}
else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
如果沒有找到對應的HandlerExecutionChain則會拋出異常NoHandlerFoundException,在開發(fā)的過程中,如果我們將具體的URL寫錯了則會遇到這個404錯誤。
繼續(xù)查看doDispatch()方法的源碼,如果找到了HandlerExecutionChain接下來會調(diào)用getHandlerAdapter()方法來查找能夠對Handler進行處理的HandlerAdapter,查看其源碼如下:
/**
* Return the HandlerAdapter for this handler object.
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
與HandlerMapping類似,查找能夠處理具體Handler的HandlerAdapter時同樣會遍歷所有配置了的HandlerAdapter,HandlerAdapter是一個接口包含一個support()方法,該方法根據(jù)Handler是否實現(xiàn)某個特定的接口來判斷該HandlerAdapter是否能夠處理這個具體的Handler,這里使用適配器模式,通過這樣的方式就可以支持不同類型的HandlerAdapter。如果沒有查找到能夠處理Handler的HandlerAdapter則會拋出異常,如果在開發(fā)的過程中Handler在實現(xiàn)接口時出現(xiàn)了問題就可能會遇到上述異常。
查找到了對應的HandlerAdapter后就會調(diào)用HandlerExecutionChain的applyPreHandle()方法來執(zhí)行配置的所有HandlerInteceptor的preHandle()方法,查看其源碼如下:
/**
* Apply preHandle methods of registered interceptors.
* @return {@code true} if the execution chain should proceed with the
* next interceptor or the handler itself. Else, DispatcherServlet assumes
* that this interceptor has already dealt with the response itself.
*/
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
HandlerExecutionChain的applyPreHandle()方法會按照順序依次調(diào)用HandlerInterceptor的preHandle()方法,但當任一HandlerInterceptor的preHandle()方法返回了false就不再繼續(xù)執(zhí)行其他HandlerInterceptor的preHandle()方法,而是直接跳轉執(zhí)行triggerAfterCompletion()方法,查看該方法源碼如下:
/**
* Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
* Will just invoke afterCompletion for all interceptors whose preHandle invocation
* has successfully completed and returned true.
*/
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
這里遍歷的下標為interceptorIndex,該變量在前一個方法applyPreHandle()方法中賦值,如果preHandle()方法返回true該變量加一,因此該方法會逆序執(zhí)行所有preHandle()方法返回了true的HandlerInterceptor的afterCompletion()方法。到這里讀者已經(jīng)掌握了HandlerInterceptor的preHandle()方法以及afterCompletion()方法的執(zhí)行順序,這些內(nèi)容并不需要我們死記,需要知道其執(zhí)行順序查看源碼是最好的方法。
繼續(xù)閱讀doDispatch()方法的源碼,如果所有攔截器的preHandle()方法都返回了true沒有進行攔截,接下來前端控制器會請求執(zhí)行上文獲取的Handler,這個Handler就是開發(fā)的時候編寫的Controller,根據(jù)實現(xiàn)接口的不同執(zhí)行相關方法,并獲取到ModelAndView類的對象。
接下來會執(zhí)行HandlerInterceptor的postHandle()方法,具體源碼如下:
/**
* Apply postHandle methods of registered interceptors.
*/
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
可以發(fā)現(xiàn),postHandle()方法是按照逆序執(zhí)行。
執(zhí)行完postHandle()方法后,doDispatch()方法調(diào)用了processDispatchResult()方法,其源碼如下:
/**
* Handle the result of handler selection and handler invocation, which is
* either a ModelAndView or an Exception to be resolved to a ModelAndView.
*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
//判斷HandlerMapping、HandlerAdapter處理時的異常是否為空
if (exception != null) {
//上述兩個組件處理時的異常不為空
//如果為ModelAndViewDefiningException異常,則獲取一個異常視圖
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
//如果不為ModelAndViewDefiningException異常,進行異常視圖的獲取
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
//判斷mv是否為空,不管是正常的ModelAndView還是異常的ModelAndView,只要存在mv就進行視圖渲染
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
//否則記錄無視圖
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
//執(zhí)行相關HandlerInterceptor的afterCompletion()方法
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
該方法傳入了一個異常類的對象dispatchException,閱讀doDispatch()方法的源碼可以看出,Spring MVC對整個doDispatch()方法用了嵌套的try-catch語句,內(nèi)層的try-catch用于捕獲HandlerMapping進行映射查找HandlerExecutionChain以及HandlerAdapter執(zhí)行具體Handler時的處理異常,并將異常傳入到上述processDispatchResult()方法中。
processDispatchResult()方法主要用于針對產(chǎn)生的異常來構造異常視圖,接著不管視圖是正常視圖還是異常視圖均調(diào)用render()方法來渲染,查看render()方法的具體源碼如下:
/**
* Render the given ModelAndView.
* <p>This is the last stage in handling a request. It may involve resolving the view by name.
* @param mv the ModelAndView to render
* @param request current HTTP servlet request
* @param response current HTTP servlet response
* @throws ServletException if view is missing or cannot be resolved
* @throws Exception if there's a problem rendering the view
*/
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
View view;
if (mv.isReference()) {
// We need to resolve the view name.
// 解析視圖名稱獲取對應視圖View
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
//如果視圖View為空拋出異常
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
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() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
//設置Http響應狀態(tài)字
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
//調(diào)用視圖View的render方法通過Model來渲染視圖
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()方法通過調(diào)用resolveViewName()方法根據(jù)視圖名稱解析對應的視圖View,該方法源碼如下:
/**
* Resolve the given view name into a View object (to be rendered).
* <p>The default implementations asks all ViewResolvers of this dispatcher.
* Can be overridden for custom resolution strategies, potentially based on
* specific model attributes or request parameters.
* @param viewName the name of the view to resolve
* @param model the model to be passed to the view
* @param locale the current locale
* @param request current HTTP servlet request
* @return the View object, or {@code null} if none found
* @throws Exception if the view cannot be resolved
* (typically in case of problems creating an actual View object)
* @see ViewResolver#resolveViewName
*/
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
resolveViewName()方法通過遍歷配置的所有ViewResolver類根據(jù)視圖名稱來解析對應的視圖View,如果找到則返回對應視圖View,沒有找到則返回null。
回到前一個render()方法,如果上述方法返回的視圖為null則拋出異常,這個異常相信大多數(shù)人也見過,當開發(fā)時寫錯了返回的View視圖名稱時就會拋出該異常。接下來調(diào)用具體視圖的render()方法來進行Model數(shù)據(jù)的渲染填充,最終構造成完整的視圖。
到這里,doDispatch()的外層try-catch異常的作用我們就知道了,為了捕獲渲染視圖時的異常,通過兩層嵌套的try-catch,Spring MVC就能夠捕獲到三大組件在處理用戶請求時的異常,通過這樣的方法能夠很方便的實現(xiàn)統(tǒng)一的異常處理。
總結
通過前文的源碼分析,我們能夠清楚的認識到Spring MVC對用戶請求的處理過程,進一步加深對Spring MVC的理解。
備注
由于作者水平有限,難免出現(xiàn)紕漏,如有問題還請不吝賜教。