不少人会用 SpringMVC,但对它的处理请求的方式并不清除,当咱们学习一个知识的时候,了解它会让咱们更好地使用它,下面咱们来看看 SpringMVC 是如何处理请求的。前端
先上图:java
Spring MVC 框架也是一个基于请求驱动的 Web 框架,而且使用了前端控制器模式(是用来提供一个集中的请求处理机制,全部的请求都将由一个单一的处理程序处理来进行设计,再根据请求映射规则分发给相应的页面控制器(动做/处理器)进行处理。首先让咱们总体看一下 Spring MVC 处理请求的流程:ios
HttpMessageConveter:将请求消息(如 Json、xml 等数据)转换成一个对象,将对象转换为指定的响应信息;web
数据转换:对请求消息进行数据转换。如 String 转换成 Integer、Double 等;spring
数据格式化:如将字符串转换成格式化数字或格式化日期等;缓存
数据验证: 验证数据的有效性(长度、格式等),验证结果存储到 BindingResult 或 Error 中);app
最全的java学习资料→q 1080355292(入群暗号:1010)
对于 SpringMVC 项目全部的请求入口(静态资源除外)这里都是从 web.xml 文件配置的前端控制器 DispatcherServlet 开始:框架
<!-- servlet请求分发器 --> <servlet> <servlet-name>springMvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:servlet-context.xml</param-value> </init-param> <!-- 表示启动容器时初始化该Servlet --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMvc</servlet-name> <!-- 这是拦截请求, /表明拦截全部请求,拦截全部.do请求 --> <url-pattern>/</url-pattern> </servlet-mapping>
DispatcherServlet UML继承关系图以下:less
这里关注蓝线部分继承结构:DispatcherServlet-->FrameworkServlet-->HttpServletBean-->HttpServlet-->GenericServlet-->Servlet,对于请求核心时序图以下:异步
对于 web 请求的处理,你们都知道是经过继承 HttpServlet 重写其 service 方法,这里打开 DispatcherServlet 源码发现这里并无看到咱们要找的 service 方法,此时到父类 FrameworkServlet 查找以下:能够看到父类重写 HttpServlet service 方法。
/** * Override the parent class implementation in order to intercept PATCH requests. */ @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); if (httpMethod == HttpMethod.PATCH || httpMethod == null) { processRequest(request, response); } else { super.service(request, response); } }
从源码分析来看当请求方法为 patch 请求或者为 null 时执行 processRequest0 方法,其余状况则调用父类 service 方法,你们都知道 SpringMVC 请求大多请求是 get|post 请求为主,此时继续向上查看 FrameworkServlet 父类 HttpServletBean(抽象类继承 HttpServlet 并未重写 service 方法,因此向上继续寻找)--> HttpServlet service 方法:
@Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; if (!(req instanceof HttpServletRequest && res instanceof HttpServletResponse)) { throw new ServletException("non-HTTP request or response"); } request = (HttpServletRequest) req; response = (HttpServletResponse) res; service(request, response); } } protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); if (ifModifiedSince < lastModified) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { // // Note that this means NO servlet supports whatever // method was requested, anywhere on this server. // String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } }
能够看到 HttpServlet service 进行了重载,根据不一样的请求类型而后调用不一样处理方法,这里以 get 请求为例,当请求方法为 get 请求时在重载 service 方法中调用 doGet 方法进行处理,这里须要特别注意的是:HttpServlet 存在 doGet 方法实现,然而在继承的子类中也存在 doGet 方法实现,到底调用哪一个方法?很明显调用子类的 doGet 方法(面向对象多态思想!!!)从继承 UML 关系图上看,最外层子类实现 doGet 方法的为 FrameworkServlet :
@Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 系统计时开始时间 long startTime = System.currentTimeMillis(); Throwable failureCause = null; // 国际化 LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = buildLocaleContext(request); //构建ServletRequestAttributes对象 RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); //异步管理 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); //初始化ContextHolders initContextHolders(request, localeContext, requestAttributes); try { doService(request, response); } catch (ServletException | IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { //恢复原来的LocaleContext和ServiceRequestAttributes到LocaleContextHolder和RequestContextHolder,避免影响Servlet之外的处理,如Filter resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } logResult(request, response, failureCause, asyncManager); //发布ServletRequestHandlerEvent消息,这个请求是否执行成功都会发布消息的 publishRequestHandledEvent(request, response, startTime, failureCause); } } // initContextHolders(request, localeContext, requestAttributes); private void initContextHolders(HttpServletRequest request, @Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) { if (localeContext != null) { LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable); } if (requestAttributes != null) { RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); } }
该方法大概作了这几件事:国际化的设置,建立 ServletRequestAttributes 对象,初始化上下文 holders (即将 Request 对象放入到线程上下文中,如后续想要在方法中获取 request、response对象此时能够经过调用 LocaleContextHolder 对应方法便可),而后调用 doService 方法。对于 doService 方法,FrameworkServlet 类并未提供实现,该方法由 DispatcherServlet 子类实现:
DispatcherServlet 里面执行处理的入口方法是 doService,因为这个类继承于 FrameworkServlet 类,重写了 doService() 方法:
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { logRequest(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<>(); 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)); } } } //Spring上下文 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()); //重定向的数据 if (this.flashMapManager != null) { 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 { //request设置完相关的属性作真正的请求处理 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); } } } }
整个方法看下来处理的操做有:处理 include 标签的请求,将上下文放到 request 的属性中,将国际化解析器放到 request 的属性中,将主题解析器放到 request 属性中,将主题放到 request 的属性中,处理重定向的请求数据最后调用 doDispatch 这个核心的方法对请求进行处理:
该方法是在 doService 方法中调用的,从底层设计了整个请求的处理流程:
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 { // 校验是否为上传请求 是上传请求执行解析 不然返回request processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 根据访问的Handler 返回指定对应的HandlerExecutionChain对象 这里从HandlerMapping 集合中查找 HandlerExecutionChain 对象包含Handler与拦截器HandlerInterceptor列表 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // 根据获得的Handler 获取对应的HandlerAdaptor对象 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 处理GET、HEAD请求的Last-Modified String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); //当数据没有更改时,就直接返回上次的数据,提升效率 if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } //执行Interceptor的preHandle if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 执行Handler 返回ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //若是须要异步处理,直接返回 if (asyncManager.isConcurrentHandlingStarted()) { return; } //当view为空时,根据request设置默认view,如Handler返回值为void applyDefaultViewName(processedRequest, mv); //执行相应Interceptor的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); } //处理返回结果,包括处理异常、渲染页面,发出完成通知触发Interceptor的afterCompletion 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); } } } }
processDispatchResult 方法主要用来处理前面返回的结果,其中包括处理异常、渲染页面、触发 Interceptor 的 afterCompletion 方法三部份内容,处理的异常是在处理请求 doDispatch 方法的过程当中产生。
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable 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); } } //执行页面渲染操做 if (mv != null && !mv.wasCleared()) { render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isTraceEnabled()) { logger.trace("No view rendering, null ModelAndView returned."); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Concurrent handling started during a forward return; } // Handler请求处理完,触发Interceptor的afterCompletion if (mappedHandler != null) { // Exception (if any) is already handled.. mappedHandler.triggerAfterCompletion(request, response, null); } }
render 视图渲染:
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 != null ? this.localeResolver.resolveLocale(request) : request.getLocale()); response.setLocale(locale); View view; String viewName = mv.getViewName(); if (viewName != null) { // We need to resolve the view name. 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 { // 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() + "'"); } } if (logger.isTraceEnabled()) { logger.trace("Rendering view [" + view + "] "); } try { if (mv.getStatus() != null) { response.setStatus(mv.getStatus().value()); } // 渲染页面处理 view.render(mv.getModelInternal(), request, response); } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug("Error rendering view [" + view + "]", ex); } throw ex; } }
今天咱们来了解了一下 SpringMVC 框架中 MVC 核心思想,SpringMVC 内部请求流程分析以及源码级别代码解读,让你们真正可以从底层级别理解整个框架执行原貌,最后以一张图来总结今天的源码分析执行流程。
模型-视图-控制器(MVC)是一个众所周知的以设计界面应用程序为基础的设计思想。它主要经过分离模型、视图及控制器在应用程序中的角色将业务逻辑从界面中解耦。一般,模型负责封装应用程序数据在视图层展现。视图仅仅只是展现这些数据,不包含任何业务逻辑。控制器负责接收来自用户的请求,并调用后台服务(service或者dao)来处理业务逻辑。处理后,后台业务层可能会返回了一些数据在视图层展现。控制器收集这些数据及准备模型在视图层展现。MVC模式的核心思想是将业务逻辑从界面中分离出来,容许它们单独改变而不会相互影响。