前端控制器
前端控制器,即所謂的Front Controller,體現的是設計模式中的前端控制器模式。前端控制器處理所有從用戶過來的請求。所有用戶的請求都要通過前端控制器。SpringMVC框架和其他請求驅動的表示層框架一樣,也是圍繞一個將請求分發到相應控制器的核心Servlet來設計的。DispatcherServlet和其他框架中的Servlet不一樣的地方在於,它和Spring容器無縫整合在了一起,因此你可以在SpringMVC中使用Spring容器所有的特性。
DispatcherServlet這個前端控制器,在SpringMVC中的作用,以官方文檔中的配圖來說明:

整個流程可以被大致描述為:一個http請求到達服務器,被DispatcherServlet接收。DispatcherServlet將請求委派給合適的處理器Controller,此時處理控制權到達Controller對象。Controller內部完成請求的數據模型的創建和業務邏輯的處理,然后再將填充了數據后的模型即model和控制權一並交還給DispatcherServlet,委派DispatcherServlet來渲染響應。DispatcherServlet再將這些數據和適當的數據模版視圖結合,向Response輸出響應。
DispatcherServlet
SpringMVC完成初始化流程之后,就進入Servlet標准生命周期的第二個階段,即“service”階段。在“service”階段中,每一次Http請求到來,容器都會啟動一個請求線程,通過service()方法,委派到doGet()或者doPost()這些方法,完成Http請求的處理。
在初始化流程中,SpringMVC巧妙的運用依賴注入讀取參數,並最終建立一個與容器上下文相關聯的Spring子上下文。這個子上下文,就像Struts2中xwork容器一樣,為接下來的Http處理流程中各種編程元素提供了容身之所。如果說將Spring上下文關聯到Servlet容器中,是SpringMVC框架的第一個亮點,那么在請求轉發流程中,SpringMVC對各種處理環節編程元素的抽象,就是另外一個獨具匠心的亮點。
Struts2采取的是一種完全和Web容器隔離和解耦的事件機制。諸如Action對象、Result對象、Interceptor對象,這些都是完全脫離Servlet容器的編程元素。Struts2將數據流和事件處理完全剝離開來,從Http請求中讀取數據后,下面的事件處理流程就只依賴於這些數據,而完全不知道有Web環境的存在。
反觀SpringMVC,無論HandlerMapping對象、HandlerAdapter對象還是View對象,這些核心的接口所定義的方法中,HttpServletRequest和HttpServletResponse對象都是直接作為方法的參數出現的。這也就意味着,框架的設計者,直接將SpringMVC框架和容器綁定到了一起。或者說,整個SpringMVC框架,都是依托着Servlet容器元素來設計的。下面就來看一下,源碼中是如何體現這一點的。
請求轉發的入口
就像任何一個注冊在容器中的Servlet一樣,DispatcherServlet也是通過自己的service()方法來接收和轉發Http請求到具體的doGet()或doPost()這些方法的。以一次典型的GET請求為例,經過HttpServlet基類中service()方法的委派,請求會被轉發到doGet()方法或者doPost()方法中。
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); long lastModified; if (method.equals("GET")) { lastModified = this.getLastModified(req); if (lastModified == -1L) { this.doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader("If-Modified-Since"); if (ifModifiedSince < lastModified) { this.maybeSetLastModified(resp, lastModified); this.doGet(req, resp); } else { resp.setStatus(304); } } } else if (method.equals("HEAD")) { lastModified = this.getLastModified(req); this.maybeSetLastModified(resp, lastModified); this.doHead(req, resp); } else if (method.equals("POST")) { this.doPost(req, resp); } else if (method.equals("PUT")) { this.doPut(req, resp); } else if (method.equals("DELETE")) { this.doDelete(req, resp); } else if (method.equals("OPTIONS")) { this.doOptions(req, resp); } else if (method.equals("TRACE")) { this.doTrace(req, resp); } else { String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[]{method}; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(501, errMsg); } }
doGet() 和 doPost() 方法,在DispatcherServlet的父類FrameworkServlet類中被覆寫。
protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.processRequest(request, response); } protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.processRequest(request, response); }
可以看到,這里只是簡單的轉發到processRequest()這個方法。
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; // Expose current LocaleResolver and request as LocaleContext. LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable); // Expose current RequestAttributes to current thread. RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = null; if (previousRequestAttributes == null || previousRequestAttributes.getClass().equals(ServletRequestAttributes.class)) { requestAttributes = new ServletRequestAttributes(request); RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); } if (logger.isTraceEnabled()) { logger.trace("Bound request context to thread: " + request); } try { doService(request, response); } finally { // Clear request attributes and reset thread-bound context. LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable); if (requestAttributes != null) { RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable); requestAttributes.requestCompleted(); } if (this.publishEvents) { // Whether or not we succeeded, publish an event. long processingTime = System.currentTimeMillis() - startTime; this.webApplicationContext.publishEvent( new ServletRequestHandledEvent(this, request.getRequestURI(), request.getRemoteAddr(), request.getMethod(), getServletConfig().getServletName(), WebUtils.getSessionId(request), getUsernameForRequest(request), processingTime, failureCause)); } } }
可以看到,processRequest()方法只是做了一些線程安全的隔離,真正的請求處理,發生在doService()方法中。點開FrameworkServlet類中的doService()方法。
protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception;
又是一個抽象方法,這也是SpringMVC類設計中的慣用伎倆:父類抽象處理流程,子類給予具體的實現。真正的實現是在DispatcherServlet類中。
讓我們接着看DispatcherServlet類中實現的doService()方法。
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { if (logger.isDebugEnabled()) { String requestUri = urlPathHelper.getRequestUri(request); logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() + " request for [" + requestUri + "]"); } // 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)) { logger.debug("Taking snapshot of request attributes before include"); attributesSnapshot = new HashMap<String, Object>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // Make framework objects available to handlers and view objects. 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 { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } }
幾個requet.setAttribute()方法的調用,將前面在初始化流程中實例化的對象設置到http請求的屬性中,供下一步處理使用,其中有容器的上下文對象、本地化解析器等SpringMVC特有的編程元素。不同於Struts2中的ValueStack,SpringMVC的數據並沒有從HttpServletRequest對象中抽離出來再存進另外一個編程元素,這也跟SpringMVC的設計思想有關。因為從一開始,SpringMVC的設計者就認為,不應該將請求處理過程和Web容器完全隔離。
所以,你可以看到,真正發生請求轉發的方法doDispatch()中,它的參數是HttpServletRequest和HttpServletResponse對象。這給我們傳遞的意思也很明確,從request中能獲取到一切請求的數據,從response中,我們又可以往服務器端輸出任何響應,Http請求的處理,就應該圍繞這兩個對象來設計。我們不妨可以將SpringMVC這種設計方案,是從Struts2的過度設計中吸取教訓,而向Servlet編程的一種回歸和簡化。
而對請求的處理交給doDispatcher方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { // 處理文件上傳 processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 決定當前請求的Handler mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // 決定當前請求的HandlerAdapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 處理last-modified請求頭 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; } } // 攔截器的前置處理 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Handler實際執行請求 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } // 設置默認視圖名 applyDefaultViewName(processedRequest, mv); // 攔截器后置處理 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); } // 選擇視圖並渲染視圖 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 if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
先看doDispatcher方法執行的主要操作時序圖

請求路由
getHandler方法就是從HandlerMapping中查詢匹配當前request的Handler。我們看到只要一匹配上 handler 就不再循環,直接返回
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { for (HandlerMapping hm : this.handlerMappings) { HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } return null; }
HandlerMapping的getHandler方法在抽象基類AbstractHandlerMapping
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 由子類根據request獲取Handler Object handler = getHandlerInternal(request); // 如果沒匹配到,則獲取默認Handler if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // 如果返回的Handler為String,則使用Spring容器實例化 if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } // 查詢匹配的攔截器,組裝Handler生成HandlerExecutionChain HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
最終返回的Handler是由攔截器鏈和Handler共同組成的,而具體匹配Handler的方法是交給子類來完成的。上一章組件初始化中提到生產環境下使用的是RequestMappingHandlerMapping,getHandlerInternal方法的實現在它的基類AbstractHandlerMethodMapping。
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 從request獲取匹配url String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); if (logger.isDebugEnabled()) { logger.debug("Looking up handler method for path " + lookupPath); } this.mappingRegistry.acquireReadLock(); try { // 查詢匹配的HandlerMethod HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); if (logger.isDebugEnabled()) { if (handlerMethod != null) { logger.debug("Returning handler method [" + handlerMethod + "]"); } else { logger.debug("Did not find handler method for [" + lookupPath + "]"); } } return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
可以看到返回的Handler的類型為HandlerMethod,它對應於Controller中的方法。上一章也提過,在AbstractHandlerMethodMapping中有一個MappingRegistry,統一管理URL和Controller方法的映射關系,lookupHandlerMethod就是對MappingRegistry的操作。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<Match>(); // 從mappingRegistry獲取匹配到的RequestMappingInfo List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // No choice but to go through all mappings... addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); } // 對匹配項進行排序 if (!matches.isEmpty()) { Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); Collections.sort(matches, comparator); if (logger.isTraceEnabled()) { logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches); } Match bestMatch = matches.get(0); if (matches.size() > 1) { if (CorsUtils.isPreFlightRequest(request)) { return PREFLIGHT_AMBIGUOUS_MATCH; } Match secondBestMatch = matches.get(1); if (comparator.compare(bestMatch, secondBestMatch) == 0) { Method m1 = bestMatch.handlerMethod.getMethod(); Method m2 = secondBestMatch.handlerMethod.getMethod(); throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" + m1 + ", " + m2 + "}"); } } handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.handlerMethod; } else { // 無匹配項處理 return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } }
通過mappingRegistry匹配返回RequestMappingInfo,對應於每個有@RequestMapping注解解析后的Method。
我們來看看,HandlerExecutionChain類的代碼。
package org.springframework.web.servlet; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.springframework.util.CollectionUtils; public class HandlerExecutionChain { private final Object handler; private HandlerInterceptor[] interceptors; private List<HandlerInterceptor> interceptorList; public HandlerExecutionChain(Object handler) { this(handler, null); } public HandlerExecutionChain(Object handler, HandlerInterceptor[] interceptors) { if (handler instanceof HandlerExecutionChain) { HandlerExecutionChain originalChain = (HandlerExecutionChain) handler; this.handler = originalChain.getHandler(); this.interceptorList = new ArrayList<HandlerInterceptor>(); CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList); CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList); } else { this.handler = handler; this.interceptors = interceptors; } } public Object getHandler() { return this.handler; } public void addInterceptor(HandlerInterceptor interceptor) { initInterceptorList(); this.interceptorList.add(interceptor); } public void addInterceptors(HandlerInterceptor[] interceptors) { if (interceptors != null) { initInterceptorList(); this.interceptorList.addAll(Arrays.asList(interceptors)); } } private void initInterceptorList() { if (this.interceptorList == null) { this.interceptorList = new ArrayList<HandlerInterceptor>(); } if (this.interceptors != null) { this.interceptorList.addAll(Arrays.asList(this.interceptors)); this.interceptors = null; } } public HandlerInterceptor[] getInterceptors() { if (this.interceptors == null && this.interceptorList != null) { this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]); } return this.interceptors; } }
一個攔截器列表,一個執行對象,這個類的內容十分的簡單,它蘊含的設計思想,卻十分的豐富。
1.攔截器組成的列表,在執行對象被調用的前后,會依次執行。這里可以看成是一個的AOP環繞通知,攔截器可以對處理對象隨心所欲的進行處理和增強。這里明顯是吸收了Struts2中攔截器的設計思想。這種AOP環繞式的擴展點設計,也幾乎成為所有框架必備的內容。
2.實際的處理對象,即handler對象,是由Object對象來引用的。
private final Object handler;
當我們拿到HandlerExecutionChain,就完成了request到Controller的路由操作。
適配器匹配
有了Handler后,需要合適的HandlerAdapter對其進行操作,因而就要根據Handler進行匹配。
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"); }
HandlerAdapter接口中定義了supports方法,用於檢測是否支持Handler。生產環境使用的RequestMappingHandlerAdapter在其基類AbstractHandlerMethodAdapter中實現了supports方法。
public final boolean supports(Object handler) { return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); }
supportsInternal方法在RequestMappingHandlerAdapter的實現里默認返回true。因而RequestMappingHandlerAdapter就是用來支持類型為HandlerMethod的Handler的處理的。
攔截器處理
在SpringMVC中的攔截器接口HandlerInterceptor中定義了三個方法
public interface HandlerInterceptor { // 在Handler找到后,執行前攔截 boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; // 在Handler執行后,視圖渲染前攔截 void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception; // 請求處理完成,視圖渲染后執行資源清理等 void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception; }
可以很清晰地對應到doDispatcher方法中。需要注意的有幾點
- 前置處理preHandle,返回值為boolean。如果返回true,則執行下一個,如果返回false,則認為當前攔截器完成了請求,DispatcherServlet會直接返回,在返回前會調用所有攔截器的afterCompletion方法,完成清理工作。
- afterCompletion方法在遇到任何情況時都需要被執行,無論是成功返回還是拋出異常。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = this.getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) { HandlerInterceptor interceptor = interceptors[i]; //遍歷執行所有攔截器的preHandle方法 if (!interceptor.preHandle(request, response, this.handler)) { this.triggerAfterCompletion(request, response, (Exception)null); return false; } } } return true; }
執行請求
HandlerAdapter的handle方法完成請求的真正執行。在AbstractHandlerMethodAdapter中由handleInternal執行。
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; checkRequest(request); // 執行HandlerMethod mav = invokeHandlerMethod(request, response, handlerMethod); // 處理緩存 if (!response.containsHeader(HEADER_CACHE_CONTROL)) { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } } return mav; }
在invokeHandlerMethod中,HandlerMethod被封裝ServletInvocableHandlerMethod,包裹上方法執行需要的信息。
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response); try { WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); // 封裝HandlerMethod ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); invocableMethod.setDataBinderFactory(binderFactory); invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); modelFactory.initModel(webRequest, mavContainer, invocableMethod); mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); // 異步請求處理 AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); asyncWebRequest.setTimeout(this.asyncRequestTimeout); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.setTaskExecutor(this.taskExecutor); asyncManager.setAsyncWebRequest(asyncWebRequest); asyncManager.registerCallableInterceptors(this.callableInterceptors); asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); if (asyncManager.hasConcurrentResult()) { Object result = asyncManager.getConcurrentResult(); mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; asyncManager.clearConcurrentResult(); if (logger.isDebugEnabled()) { logger.debug("Found concurrent result value [" + result + "]"); } invocableMethod = invocableMethod.wrapConcurrentResult(result); } // 執行處理 invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } // 封裝數據和視圖 return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); } }
再到ServletInvocableHandlerMethod的invokeAndHandle方法
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 執行request Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); if (returnValue == null) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { mavContainer.setRequestHandled(true); return; } } else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true); return; } mavContainer.setRequestHandled(false); try { // 對返回值進行處理 this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex); } throw ex; } } public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 執行方法參數 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) + "' with arguments " + Arrays.toString(args)); } Object returnValue = doInvoke(args); if (logger.isTraceEnabled()) { logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) + "] returned [" + returnValue + "]"); } return returnValue; } protected Object doInvoke(Object... args) throws Exception { ReflectionUtils.makeAccessible(getBridgedMethod()); return getBridgedMethod().invoke(getBean(), args); }

需要說明的一點是方法執行完成的返回值通過返回值處理器HandlerMethodReturnValueHandler進行處理。在RequestMappingHandlerAdapter的初始化中,內置了眾多的HandlerMethodReturnValueHandler來處理多種類型的返回值。
在完成請求執行后,doDispatcher方法中做了一個默認View的設置。
applyDefaultViewName(processedRequest, mv); private void applyDefaultViewName(HttpServletRequest request, ModelAndView mv) throws Exception { if (mv != null && !mv.hasView()) { mv.setViewName(getDefaultViewName(request)); } }
而這個getDefaultViewName是通過RequestToViewNameTranslator的實現類來解析的
protected String getDefaultViewName(HttpServletRequest request) throws Exception { return this.viewNameTranslator.getViewName(request); }
默認實現DefaultRequestToViewNameTranslator,根據配置的一些通用url進行匹配
public String getViewName(HttpServletRequest request) { String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); return (this.prefix + transformPath(lookupPath) + this.suffix); }
視圖渲染
當請求完成后,返回的ModelAndView需要渲染到瀏覽器進行顯示。doDispatcher方法中processDispatchResult用來處理視圖。
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception { boolean errorView = false; // 異常處理 if (exception != null) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } 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? 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; } // 完成后執行攔截器的afterCompletion if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, null); } }
render方法執行渲染,最終由View實現類執行
view.render(mv.getModelInternal(), request, response);
抽象類AbstractView執行對數據進行組裝,輸出操作交由子類完成
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { if (logger.isTraceEnabled()) { logger.trace("Rendering view with name '" + this.beanName + "' with model " + model + " and static attributes " + this.staticAttributes); } // 組裝數據 Map<String, Object> mergedModel = createMergedOutputModel(model, request, response); prepareResponse(request, response); // 渲染輸出 renderMergedOutputModel(mergedModel, getRequestToExpose(request), response); }
以通用的InternalResourceView舉例
protected void renderMergedOutputModel( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { // Expose the model object as request attributes. exposeModelAsRequestAttributes(model, request); // Expose helpers as request attributes, if any. exposeHelpers(request); // Determine the path for the request dispatcher. String dispatcherPath = prepareForRendering(request, response); // Obtain a RequestDispatcher for the target resource (typically a JSP). RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath); if (rd == null) { throw new ServletException("Could not get RequestDispatcher for [" + getUrl() + "]: Check that the corresponding file exists within your web application archive!"); } // If already included or response already committed, perform include, else forward. if (useInclude(request, response)) { response.setContentType(getContentType()); if (logger.isDebugEnabled()) { logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'"); } rd.include(request, response); } else { // Note: The forwarded resource is supposed to determine the content type itself. if (logger.isDebugEnabled()) { logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'"); } rd.forward(request, response); } }
最終由java Servlet的RequestDispatcher完成輸出。其實就是做了一個跳轉
本章中以請求的正向主流程解析了DispatcherServlet及相關類完成此過程的源碼,其主要過程則是HandlerExecutionChain,HandlerMapping,HandlerAdapter,View等組件的交互過程,貼兩張網上的核心原理圖,希望對大家理解SpringMVC的原理有幫助。


