SpringMVC执行过程解析
做过java开发的人都知道Spring,就算目前不知道,慢慢也会知道,由于spring体系庞大,模块众多,我就介绍下业务开发(以多年经历的认识,90%的人都是做业务开发的)时用到的SpringMVC。
首先,让我们从 Spring MVC 的四大组件:前端控制器(DispatcherServlet)、处理器映射器(HandlerMapping)、处理器适配器(HandlerAdapter)以及视图解析器(ViewResolver) 的角度来看一下 Spring MVC 对用户请求的处理过程,
SpringMVC 执行过程
- 用户请求发送到前端控制器 DispatcherServlet。
- 前端控制器 DispatcherServlet 接收到请求后,DispatcherServlet 会使用 HandlerMapping 来处理,HandlerMapping 会查找到具体进行处理请求的 Handler 对象。
- HandlerMapping 找到对应的 Handler 之后,并不是返回一个 Handler 原始对象,而是一个 Handler 执行链(HandlerExecutionChain),在这个执行链中包括了拦截器和处理请求的 Handler。HandlerMapping 返回一个执行链给 DispatcherServlet。
- DispatcherServlet 接收到执行链之后,会调用 Handler 适配器去执行 Handler。
- HandlerAdapter执行完成 Handler之后会得到一个 ModelAndView,并返回给 DispatcherServlet。
- DispatcherServlet 接收到 HandlerAdapter 返回的 ModelAndView 之后,会根据其中的视图名调用 ViewResolver。
- ViewResolver 根据逻辑视图名解析成一个真正的 View 视图,并返回给 DispatcherServlet。
- DispatcherServlet 接收到视图之后,会根据上面的 ModelAndView 中的 model 来进行视图中数据的填充,也就是所谓的视图渲染。
- 渲染完成之后,DispatcherServlet 就可以将结果返回给用户了。
在了解了大概的执行过程后,让我们一起从源码https://github.com/spring-projects/spring-framework/角度去深入探索。
源码解析
首先当我们访问url的时候,将会把请求发送到前端控制器 DispatcherServlet,DispatcherServlet 是一个 Servlet,我们知道在 Servlet 在处理一个请求的时候会交给 service 方法进行处理,这里也不例外,DispatcherServlet 继承了 FrameworkServlet,首先进入 FrameworkServlet 的 service 方法:
1 protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 2 // 请求方法 3 HttpMethod httpMethod =HttpMethod.resolve(request.getMethod()); 4 // 若方法为 PATCH 方法或为空则单独处理 5 if (httpMethod == HttpMethod.PATCH || httpMethod == null) { 6 processRequest(request, response); 7 } else { 8 // 其他的请求类型的方法经由父类,也就是 HttpServlet 处理 9 super.service(request, response); 10 } 11 }
HttpServlet 中会根据请求类型的不同分别调用 doGet 或者 doPost 等方法,FrameworkServlet 中已经重写了这些方法,在这些方法中会调用 processRequest 进行处理,在 processRequest 中会调用 doService 方法,这个 doService 方法就是在 DispatcherServlet 中实现的。下面就看下 DispatcherServlet 中的 doService 方法的实现。
DispatcherServlet 收到请求
DispatcherServlet 中的 doService方法:
1 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { 2 logRequest(request); 3 // 给 request 中的属性做一份快照,以便能够恢复原始属性 4 Map<String, Object> attributesSnapshot = null; 5 if (WebUtils.isIncludeRequest(request)) { 6 attributesSnapshot = new HashMap<>(); 7 Enumeration<?> attrNames = request.getAttributeNames(); 8 while (attrNames.hasMoreElements()) { 9 String attrName = (String) attrNames.nextElement(); 10 if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { 11 attributesSnapshot.put(attrName, request.getAttribute(attrName)); 12 } 13 } 14 } 15 // 如果没有配置本地化或者主题的处理器之类的,SpringMVC 会使用默认的配置文件,即 DispatcherServlet.properties 16 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); 17 request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); 18 request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); 19 request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); 20 if (this.flashMapManager != null) { 21 FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); 22 if (inputFlashMap != null) { 23 request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); 24 } 25 request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); 26 request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); 27 } 28 29 try { 30 // 开始真正的处理 31 doDispatch(request, response); 32 } 33 finally { 34 if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { 35 // 恢复原始属性快照 36 if (attributesSnapshot != null) { 37 restoreAttributesAfterInclude(request, attributesSnapshot); 38 } 39 } 40 } 41 }
接下来 DispatcherServlet 开始真正的处理,让我们来看下 doDispatch 方法,首先会获取当前请求的 Handler 执行链,然后找到合适的 HandlerAdapter,接着调用 RequestMappingHandlerAdapter 的 handle 方法,如下为 doDispatch 方法:
1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { 2 HttpServletRequest processedRequest = request; 3 HandlerExecutionChain mappedHandler = null; 4 boolean multipartRequestParsed = false; 5 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 6 try { 7 ModelAndView mv = null; 8 Exception dispatchException = null; 9 try { 10 // 先检查是不是 Multipart 类型的,比如上传等;如果是 Multipart 类型的,则转换为 MultipartHttpServletRequest 类型 11 processedRequest = checkMultipart(request); 12 multipartRequestParsed = (processedRequest != request); 13 14 // 获取当前请求的 Handler 执行链 15 mappedHandler = getHandler(processedRequest); 16 if (mappedHandler == null) { 17 noHandlerFound(processedRequest, response); 18 return; 19 } 20 21 // 获取当前请求的 Handler 适配器 22 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 23 24 // 对于 header 中 last-modified 的处理 25 String method = request.getMethod(); 26 boolean isGet = "GET".equals(method); 27 if (isGet || "HEAD".equals(method)) { 28 long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); 29 if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { 30 return; 31 } 32 } 33 34 // 遍历所有定义的 interceptor,执行 preHandle 方法 35 if (!mappedHandler.applyPreHandle(processedRequest, response)) { 36 return; 37 } 38 39 // 实际调用 Handler 的地方 40 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 41 42 if (asyncManager.isConcurrentHandlingStarted()) { 43 return; 44 } 45 // 处理成默认视图名,也就是添加前缀和后缀等 46 applyDefaultViewName(processedRequest, mv); 47 // 拦截器postHandle方法进行处理 48 mappedHandler.applyPostHandle(processedRequest, response, mv); 49 } 50 catch (Exception ex) { 51 dispatchException = ex; 52 } 53 catch (Throwable err) { 54 dispatchException = new NestedServletException("Handler dispatch failed", err); 55 } 56 // 处理最后的结果,渲染之类的都在这里 57 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 58 } 59 catch (Exception ex) { 60 triggerAfterCompletion(processedRequest, response, mappedHandler, ex); 61 } 62 catch (Throwable err) { 63 triggerAfterCompletion(processedRequest, response, mappedHandler, 64 new NestedServletException("Handler processing failed", err)); 65 } 66 finally { 67 if (asyncManager.isConcurrentHandlingStarted()) { 68 if (mappedHandler != null) { 69 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); 70 } 71 } 72 else { 73 if (multipartRequestParsed) { 74 cleanupMultipart(processedRequest); 75 } 76 } 77 } 78 }
查找对应的 Handler 对象
查找对应的 Handler 对象
让我们去探索下是如何获取当前请求的 Handler 执行链,对应着这句代码 mappedHandler = getHandler(processedRequest);
,看下 DispatcherServlet 具体的 getHandler 方法,该方法主要是遍历所有的 handlerMappings 进行处理,handlerMappings 是在启动的时候预先注册好的,在循环中会调用 AbstractHandlerMapping 类中的 getHandler 方法来获取 Handler 执行链,若获取的 Handler 执行链不为 null,则返回当前请求的 Handler 执行链,DispatcherServlet 类的 getHandler 方法如下:
1 protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { 2 if (this.handlerMappings != null) { 3 // 遍历所有的 handlerMappings 进行处理,handlerMappings 是在启动的时候预先注册好的 4 for (HandlerMapping mapping : this.handlerMappings) { 5 HandlerExecutionChain handler = mapping.getHandler(request); 6 if (handler != null) { 7 return handler; 8 } 9 } 10 } 11 return null; 12 }
在循环中,根据 mapping.getHandler(request);
,继续往下看 AbstractHandlerMapping 类中的 getHandler 方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { 2 // 根据 request 获取 handler 3 Object handler = getHandlerInternal(request); 4 if (handler == null) { 5 // 如果没有找到就使用默认的 handler 6 handler = getDefaultHandler(); 7 } 8 if (handler == null) { 9 return null; 10 } 11 // 如果 Handler 是 String,表明是一个 bean 名称,需要寻找对应 bean 12 if (handler instanceof String) { 13 String handlerName = (String) handler; 14 handler = obtainApplicationContext().getBean(handlerName); 15 } 16 // 封装 Handler 执行链 17 return getHandlerExecutionChain(handler, request); 18 }
AbstractHandlerMapping 类中的 getHandler 方法中首先根据 requrst 获取 handler,主要是调用了 AbstractHandlerMethodMapping 类中的 getHandlerInternal 方法,该方法首先获取 request 中的 url,即 /testSpringMvc
,用来匹配 handler 并封装成 HandlerMethod,然后根据 handlerMethod 中的 bean 来实例化 Handler 并返回。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { 2 // 获取 request 中的 url,用来匹配 handler 3 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); 4 request.setAttribute(LOOKUP_PATH, lookupPath); 5 this.mappingRegistry.acquireReadLock(); 6 try { 7 // 根据路径寻找 Handler,并封装成 HandlerMethod 8 HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); 9 // 根据 handlerMethod 中的 bean 来实例化 Handler,并添加进 HandlerMethod 10 return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); 11 } 12 finally { 13 this.mappingRegistry.releaseReadLock(); 14 } 15 }
接下来,我们看 lookupHandlerMethod 的逻辑,主要逻辑委托给了 mappingRegistry 这个成员变量来处理:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { 2 List<Match> matches = new ArrayList<>(); 3 // 通过 lookupPath 属性中查找。如果找到了,就返回对应的RequestMappingInfo 4 List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); 5 if (directPathMatches != null) { 6 // 如果匹配到了,检查其他属性是否符合要求,如请求方法,参数,header 等 7 addMatchingMappings(directPathMatches, matches, request); 8 } 9 if (matches.isEmpty()) { 10 // 没有直接匹配到,则遍历所有的处理方法进行通配符匹配 11 addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); 12 } 13 14 if (!matches.isEmpty()) { 15 // 如果方法有多个匹配,不同的通配符等,则排序选择出最合适的一个 16 Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); 17 matches.sort(comparator); 18 Match bestMatch = matches.get(0); 19 // 如果有多个匹配的,会找到第二个最合适的进行比较 20 if (matches.size() > 1) { 21 if (logger.isTraceEnabled()) { 22 logger.trace(matches.size() + " matching mappings: " + matches); 23 } 24 if (CorsUtils.isPreFlightRequest(request)) { 25 return PREFLIGHT_AMBIGUOUS_MATCH; 26 } 27 Match secondBestMatch = matches.get(1); 28 if (comparator.compare(bestMatch, secondBestMatch) == 0) { 29 Method m1 = bestMatch.handlerMethod.getMethod(); 30 Method m2 = secondBestMatch.handlerMethod.getMethod(); 31 String uri = request.getRequestURI(); 32 // 不能有相同的最优 Match 33 throw new IllegalStateException( 34 "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}"); 35 } 36 } 37 request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod); 38 // 设置 request 参数(RequestMappingHandlerMapping 对其进行了覆写) 39 handleMatch(bestMatch.mapping, lookupPath, request); 40 // 返回匹配的 url 的处理的方法 41 return bestMatch.handlerMethod; 42 } 43 else { 44 // 调用 RequestMappingHandlerMapping 类的 handleNoMatch 方法再匹配一次 45 return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); 46 } 47 }
通过上面的过程,我们就获取到了 Handler,就开始封装执行链了,就是将我们配置的拦截器加入到执行链中去,getHandlerExecutionChain 方法如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { 2 // 如果当前 Handler 不是执行链类型,就使用一个新的执行链实例封装起来 3 HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); 4 5 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH); 6 // 遍历拦截器,找到跟当前 url 对应的,添加进执行链中去 7 for (HandlerInterceptor interceptor : this.adaptedInterceptors) { 8 if (interceptor instanceof MappedInterceptor) { 9 MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; 10 if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { 11 chain.addInterceptor(mappedInterceptor.getInterceptor()); 12 } 13 } 14 else { 15 chain.addInterceptor(interceptor); 16 } 17 } 18 return chain; 19 }
到此为止,我们就获取了当前请求的 Handler 执行链,接下来看下是如何获取请求的 Handler 适配器,主要依靠 DispatcherServlet 类的 getHandlerAdapter 方法,该方法就是遍历所有的 HandlerAdapter,找到和当前 Handler 匹配的就返回,在这里匹配到的为 RequestMappingHandlerAdapter。
DispatcherServlet 类的 getHandlerAdapter 方法如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { 2 if (this.handlerAdapters != null) { 3 // 遍历所有的 HandlerAdapter,找到和当前 Handler 匹配的就返回 4 for (HandlerAdapter adapter : this.handlerAdapters) { 5 if (adapter.supports(handler)) { 6 return adapter; 7 } 8 } 9 } 10 throw new ServletException("No adapter for handler [" + handler + 11 "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); 12 }
HandlerAdapter 执行当前的 Handler
再获取完当前请求的 Handler 适配器后,接着进行缓存处理,也就是对 last-modified 的处理,然后调用 applyPreHandle 方法执行拦截器的 preHandle 方法,即遍历所有定义的 interceptor,执行 preHandle 方法,然后就到了实际执行 handle 的地方,doDispatch 方法中 handle 方法是执行当前 Handler,我们这里使用的是 RequestMappingHandlerAdapter,首先会进入 AbstractHandlerMethodAdapter 的 handle 方法:
1 public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
2 return handleInternal(request, response, (HandlerMethod) handler);
3 }
在 AbstractHandlerMethodAdapter 的 handle 方法中又调用了 RequestMappingHandlerAdapter 类的 handleInternal 方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { 2 ModelAndView mav; 3 checkRequest(request); 4 if (this.synchronizeOnSession) { 5 HttpSession session = request.getSession(false); 6 if (session != null) { 7 Object mutex = WebUtils.getSessionMutex(session); 8 synchronized (mutex) { 9 mav = invokeHandlerMethod(request, response, handlerMethod); 10 } 11 } 12 else { 13 mav = invokeHandlerMethod(request, response, handlerMethod); 14 } 15 } 16 else { 17 // 执行方法,封装 ModelAndView 18 mav = invokeHandlerMethod(request, response, handlerMethod); 19 } 20 if (!response.containsHeader(HEADER_CACHE_CONTROL)) { 21 if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { 22 applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); 23 } 24 else { 25 prepareResponse(response); 26 } 27 } 28 return mav; 29 }
在执行完 handle 方法后,然后调用 applyDefaultViewName 方法组装默认视图名称,将前缀和后缀名都加上,接着调用 applyPostHandle 方法执行拦截器的 preHandle 方法,也就是遍历所有定义的 interceptor,执行preHandle 方法。
处理最终结果以及渲染
最终调用 DispatcherServlet 类中的 processDispatchResult 方法,此方法主要是处理最终结果的,包括异常处理、渲染页面和发出完成通知触发拦截器的 afterCompletion() 方法执行等。processDispatchResult()方法代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception { 2 boolean errorView = false; 3 if (exception != null) { 4 if (exception instanceof ModelAndViewDefiningException) { 5 logger.debug("ModelAndViewDefiningException encountered", exception); 6 mv = ((ModelAndViewDefiningException) exception).getModelAndView(); 7 } 8 else { 9 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); 10 mv = processHandlerException(request, response, handler, exception); 11 errorView = (mv != null); 12 } 13 } 14 if (mv != null && !mv.wasCleared()) { 15 // 渲染 16 render(mv, request, response); 17 if (errorView) { 18 WebUtils.clearErrorRequestAttributes(request); 19 } 20 } 21 else { 22 if (logger.isTraceEnabled()) { 23 logger.trace("No view rendering, null ModelAndView returned."); 24 } 25 } 26 if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { 27 return; 28 } 29 if (mappedHandler != null) { 30 mappedHandler.triggerAfterCompletion(request, response, null); 31 } 32 }
接下来让我们看下 DispatcherServlet 类的 render 方法是如何完成渲染的,DispatcherServlet 类的 render 方法渲染过程如下:
- 判断 ModelAndView 中 view 是否为 view name,没有获取其实例对象:如果是根据 name,如果是则需要调用 resolveViewName 从视图解析器获取对应的视图(View)对象;否则 ModelAndView 中使用 getview 方法获取 view 对象。
- 然后调用 View 类的 render 方法。
DispatcherServlet 类的 render 方法如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { 2 // 设置本地化 3 Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale()); 4 response.setLocale(locale); 5 View view; 6 String viewName = mv.getViewName(); 7 if (viewName != null) { 8 // 解析视图名,得到视图 9 view = resolveViewName(viewName, mv.getModelInternal(), locale, request); 10 if (view == null) { 11 throw new ServletException("Could not resolve view with name '" + mv.getViewName() + 12 "' in servlet with name '" + getServletName() + "'"); 13 } 14 } 15 else { 16 view = mv.getView(); 17 if (view == null) { 18 throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " + 19 "View object in servlet with name '" + getServletName() + "'"); 20 } 21 } 22 23 if (logger.isTraceEnabled()) { 24 logger.trace("Rendering view [" + view + "] "); 25 } 26 try { 27 if (mv.getStatus() != null) { 28 response.setStatus(mv.getStatus().value()); 29 } 30 // 委托给视图进行渲染 31 view.render(mv.getModelInternal(), request, response); 32 } 33 catch (Exception ex) { 34 if (logger.isDebugEnabled()) { 35 logger.debug("Error rendering view [" + view + "]", ex); 36 } 37 throw ex; 38 } 39 }
假设我们用的是 Thymeleaf 模版引擎(也有其他模板引擎比如freemarker等),所以 view.render 找到对应的视图 ThymeleafView 的 render 方法进行渲染。
1 public void render(final Map<String, ?> model, final HttpServletRequest request, final HttpServletResponse response) throws Exception { 2 renderFragment(this.markupSelectors, model, request, response); 3 }
ThymeleafView 的 render 方法又调用 renderFragment 方法进行视图渲染,渲染完成之后,DispatcherServlet 就可以将结果返回给我们了。
总结
springmvc的执行流程简单介绍完了(不足之处多多指教),web框架的处理流程可以说一样的思路(本人以前用django,python的web框架)做过开发,如果不是看编程语言的话,会以为说的是django流程呢!!!,都是为了快速开发快速迭代业务系统,不像早期需要编写原生的servlet,比如
参考:
0 . Servlet 工作原理解析
https://www.ibm.com/developerworks/cn/java/j-lo-servlet/
1. What is the use of DispatcherServlet in Spring MVC? Interview Question https://www.java67.com/2017/06/what-is-use-of-dispatcherservlet-in-spring-mvc.html?m=1
2. How does DispatcherServlet process a request in Spring MVC?
3. Web on Servlet Stack
https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/web.html#spring-web