一、組件說明
- DispatcherServlet:前端控制器,用於請求到達前端控制器,由它調用其他組件處理用戶的請求。
- HandlerMapping:處理器映射器,負責根據用戶請求找到Handler(處理器),springmvc提供了不同的映射器實現方式。
- Handler:處理器,對具體的用戶請求進行處理。
- HandlerAdapter:處理器適配器,通過HandlerAdapter對處理器進行執行。
- View Resolver:視圖解析器,負責將處理結果生成view視圖。View Resolver首先根據邏輯視圖名解析成物理視圖名即具體的頁面地址,再生成View視圖對象,最后對View進行渲染將處理結果通過頁面展示給用戶。springmvc框架提供了很多的View視圖類型,包括:jstlView、freemarkerView、pdfView等。
二、前端控制器
DispatcherServlet是springmvc的中央調度器,DispatcherServlet創建時會默認從DispatcherServlet.properties文件加載springmvc所用的各種組件。
二、處理器映射器
處理器映射器負責根據request請求找到對應的Handler處理器以及Interceptor攔截器,將它們封裝在HandlerExecutionChain對象中返回給前端控制器。
下面是兩種常用的處理器映射器:
1、BeanNameURLHandlerMapping:根據請求的url與spring容器中定義的bean的name進行匹配,從而從spring容器中找到bean實例。
2、simpleURLHandlerMapping:它可以將url和處理器bean的id進行統一映射配置。
三、處理器適配器
HandlerAdapter會根據適配器接口對Handler進行包裝適配,包裝后即可對處理器進行執行(使用了適配器模式)。
下面是兩種常用的處理器適配器:
1、SimpleControllerHandlerAdapter:簡單控制器處理器適配器,所有實現了org.springframework.web.servlet.mvc.Controller 接口的bean通過此適配器進行適配執行。
2、HttpRequestHandlerAdapter:http請求處理器適配器,所有實現了org.springframework.web.HttpRequestHandler接口的bean通過此適配器進行適配執行。
注:處理器實現HttpRequestHandler接口,實現handleRequest方法,該方法沒有返回、ModelAndView,可以通過response修改定義響應內容,比如返回json數據。
四、注解處理器映射器和適配器
可以使用組件掃描,而省去在spring容器中配置每個controller類。配置如下:
<context:component-scan base-package="com.demo.ssm.controller"></context:component-scan>
1、注解處理器映射器,對類中標記的@RequestMapping的方法進行映射,根據RequestMapping定義的url匹配RequestMapping標記的方法,匹配成功返回HandlerMethod對象給前端控制器。
<!-- 注解映射器 --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
2、注解處理器適配器,對標記的@RequestMapping的方法進行適配。
<!-- 注解適配器 --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
注:還可以使用下面的配置代替上面處理器映射器和適配器的配置:
<mvc:annotation-driven></mvc:annotation-driven>
五、總結
springmvc處理流程:
1、用戶發送請求到DispatchServlet前端控制器。

/** * 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 { 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); // Determine handler for the current request. mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. 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; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(request, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Error err) { triggerAfterCompletionWithError(processedRequest, response, mappedHandler, 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); } } } }
2、DispatchServlet調用HandlerMapping處理器映射器根據url查找Handler。

/** * 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; }
3、DispatchServlet調用HandlerAdapter處理器適配器對HandlerMapping找到的Handler進行包裝。

/** * 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"); }
4、執行Handler,並返回一個ModelAndView。

/** * Determine an error ModelAndView via the registered HandlerExceptionResolvers. * @param request current HTTP request * @param response current HTTP response * @param handler the executed handler, or {@code null} if none chosen at the time of the exception * (for example, if multipart resolution failed) * @param ex the exception that got thrown during handler execution * @return a corresponding ModelAndView to forward to * @throws Exception if no error ModelAndView found */ protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // Check registered HandlerExceptionResolvers... ModelAndView exMv = null; for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) { exMv = handlerExceptionResolver.resolveException(request, response, handler, ex); if (exMv != null) { break; } } if (exMv != null) { if (exMv.isEmpty()) { request.setAttribute(EXCEPTION_ATTRIBUTE, ex); return null; } // We might still need view name translation for a plain error model... if (!exMv.hasView()) { exMv.setViewName(getDefaultViewName(request)); } if (logger.isDebugEnabled()) { logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex); } WebUtils.exposeErrorRequestAttributes(request, ex, getServletName()); return exMv; } throw ex; }
5、DispatchServlet調用ViewResolver視圖解析器進行視圖解析,解析完成后返回一個View,再進行視圖渲染並展示。

/** * 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 = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request); 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 { 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; } }