【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
上一篇:SpringMVC源码分析-DispatcherServlet-init方法分析
DispatcherServlet的init已经将所需要的各种Resolver准备好,可以说是万事俱备只欠东风了,下面就看看它是如何接收请求,并将请求映射到Controller上的方法,然后将返回值格式化为字符串或者使用视图解析器完成解析的
时序图
概要说明
前半部分画了从Tomcat最后一个阀门(Valve)如何一步步调用到DispatcherServlet的doService方法。
后半部分画了SpringMVC的处理流程,如下:
在DispatcherServlet中调用getHandler()得到符合条件的HandlerMapping(如:RequestMappingRequestHandler-请求参数解析、绑定。。。在这里面还会降将该请求适合的Interceptors与handler一起封装为一个HandlerExecutionChain),接着根据得到的HandlerMapping调用getHandlerAdpater()得到符合条件的HandlerAdapter(如:RequestMappingHandlerAdapter-调用Controller中的方法,返回值格式化,确定ModleAndView),在调用Controller之前执行拦截器的preHandle(),调用再接着调用HandlerAdapter的handle方法,在调用Contoller之后执行拦截器的postHandle()方法,最后调用processDispatchResult()根据MV执行转发
源码分析
DispatcherServlet-doDispatch
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);//文件上传,转换request为mutipart request multipartRequestParsed = (processedRequest != request); // 决定当前请求使用的最优Handler,也就是说根据请求路径去找到Controller中的某一个方法 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // 根据mappedHanler中的HandlerMethod决定当前请求要使用的HandlerAdpater,比如RequestMappingHandlerAdapter 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)) { //getLastModified该方法都是返回-1,也不知道具体是干什么用的。感觉像是判断Mapping是否改变了,也许和热加载有关系吧 long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } //Invoke前处理--百度一大把SpringMVC请求流程中的讲到的东西 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 真正invoke Handler对应的方法或者其他对象,会完成参数绑定、方法调用、返回参数格式化等操作 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); //Invoke后处理 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); } /** * 处理由前面选择的处理器(mappedHandler)和处理器(HandlerAdapter)调用的结果:就是一个正常或者一个异常的MV * 然后再调用triggerAfterCompletion * 下面的两个异常逻辑也需要调用triggerAfterCompletion * * */ 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); } } } }
获取Handler(HandlerExecutionChain)
DispatcherServlet.getHandle()
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { /** * 循环所有的HandlerMappings,找到合适的HandlerMethod或者其他类型的Handler * 将找到的Handler与拦截器一起封装一个HandlerExecutionChain * 下面看看RequestMappingHandlerMapping的getHandler方法 */ for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }
RequestMappingHandlerMapping.getHandler(),最终会调用到它父类AbstractHandlerMethodMapping的getHandlerInternal()
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); request.setAttribute(LOOKUP_PATH, lookupPath); this.mappingRegistry.acquireReadLock(); try { //从HandlerMappings当中找到一个最优匹配的Handler(MethodHandler) HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
上面的代码再调用AbstractHandlerMethodMapping的lookupHandlerMethod方法
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); /** * mappingRegistry当中有一个urlLookup的Map,里面存放的是所有以目录形式访问的路径对应的RequestMappingInfo * 比如:/testweb/user/query * /testweb/user/save * 但是/testweb/user/query/{userName}这种就不行了,通过这个拆分可以让更多场景迅速找到唯一的RequestMappingInfo, * 减少循环遍历所有的RequestMappingInfos,然后再Compare的耗时 */ 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()) { //MatchComparator定义了排序的规则,具体规则就不赘述了,百度一下或者看看RequestMappingInfo的CompareTo方法 Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); matches.sort(comparator); Match bestMatch = matches.get(0); if (matches.size() > 1) { if (logger.isTraceEnabled()) { logger.trace(matches.size() + " matching mappings: " + matches); } 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(); String uri = request.getRequestURI(); throw new IllegalStateException( "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}"); } } request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod); handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.handlerMethod; } else { return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } }
获取HandlerAdpter
DispathcerServlet.getHandlerAdapter()
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { if (this.handlerAdapters != null) { /** * 循环所有的handlerAdpaters,调用它们的supports方法找到匹配的HandlerAdapter * 以RequestMappingHandlerAdapter为例看看它的supports方法 */ for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(handler)) { return adapter; } } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }
上面的supports方法实际上是RequestMappingHandlerAdapter父类AbstractHandlerMethodAdapter的supports
public final boolean supports(Object handler) { //supportsInternal()里面居然就是一个返回一个确定的true,可以想到,自己可以扩展一个Adapter修改这个方法,更改MVC的默认匹配规则 return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); }
可以很清楚看到这个Adapter就是处理HandlerMethod这类型的Handler
执行拦截的preHandle
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { /** * 循环执行拦截器的preHandle方法,如果返回值为false,则还要倒序执行拦截的afterCompletion方法 */ 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; }
这里面有一个倒序执行拦截器的afterCompletion方法要注意
调用Adapter的handle
在这里重点看底层被调用的
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { /** * 方法里面首先解析参数,选择对的HandlerMethodArgumentResolver,并完成参数值的绑定,比如: * RequestParamMethodArgumentResolver负责解析,完成参数绑定。没有注解该Resolver同样适用 * 由PathVariableMethodArgumentResolver负责解析,完成参数绑定 * 由RequestBodyMethodArgumentResolver负责解析,完成参数绑定 */ Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); 。。。。省略部分代码。。。。。 try { /** * 根据返回类型以及方法的注解判断才用那个HandlerMethodReturnValueHandler来处理返回值 * * * * * */ this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(formatErrorForReturnValue(returnValue), ex); } throw ex; } }
returnValueHandlers.handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { //确定应该使用的HandlerMethodReturnValueHandler HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null) { throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName()); } //对invoke返回的结果进行处理,比如进行json格式化 handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); }
看看debug时handler是谁
再看看对返回结果转换的核心代码,可以看到默认是有前面4个Converter的,最后一个Converter是我通过PostProcessor强行添加进去的
最终由MappingJacksonHttpMessageConverter完成对User对象的Json格式化转换,并将转换结果写到了Response当中
执行拦截器的postHandle
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { /** * 循环执行拦截器的postHandle方法 */ for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(request, response, this.handler, mv); } } }
执行跳转
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false; //异常MV处理 if (exception != null) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); //在这里可以使用自定义的ExceptionHandlerResolver对异常MV进行处理 mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } //渲染Handler返回的MV,渲染没有深入去看,因为现在流行前后端分离,几乎都是返回的JSON串回去 if (mv != null && !mv.wasCleared()) { render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } 。。。省略部分代码。。。 if (mappedHandler != null) { // 最后执行afterCompletion方法 mappedHandler.triggerAfterCompletion(request, response, null); } }
最后只有执行完Tomcat的CoyoteAdapter.service中的如下两行代码浏览器才会收到返回值
request.finishRequest(); response.finishResponse();
总结
DispatcherServlet接收请求的处理流程涉及到几个非常重要的扩展点,自定义HandlerMapping、自定义HandlerAdapter,通常SpringMVC是不建议开发者去修改最底层的内容的,但是SpringMVC允许且建议开发者可以修改默认的HandlerMapping与HandlerAdpater当中属性中的值,比如给Adapter添加argumentResolvers,initBinderArgumentResolvers,returnValueHandlers,MessageConverters等等
除了上面的内容,当然还有经常用到的一点就是Interceptor在HandlerAdapter.handle()之前的preHandle与之后postHandle,以及afterCompletion。需要注意preHandle的返回值为false时会倒序调用拦截器中的afterCompletion方法
SpringMVC源码复杂度和Spring比起来,前者还是要轻松一些,总的来说在Tomcat启动的时候调用DispatcherServlet.init方法通过Spring容器完成了所有默认组件与配置类的实例化与参数的加载,开发者可以通过Spring提供的BeanPostProcessor修改这些默认类的实例化过程,修改它们的属性值和行为,然后在完成Dispatcher.init之后通过servlet规范调用到DispatcherServlet的service方法,通过之前已经实例化的组件与配置类实例共同作用完成了从请求到Controller的执行与返回浏览器的过程
接下来去开启SpringBoot的源码分析
来源:oschina
链接:https://my.oschina.net/u/3049601/blog/3145081