一次GET请求在springmvc中是的处理流程

    科技2022-07-13  133

    1:入口

    我们知道根据servlet规范,servlet调用的入口方法是service,在springmvc中的这个servlet就是org.springframework.web.servlet.DispatcherServlet,但是我们看其源码会发现,并没有这个入口的service方法,实际上该方法是在其父类org.springframework.web.servlet.FrameworkServlet中,源码如下: org.springframework.web.servlet.FrameworkServlet#service

    @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); } }

    因为本文分析的是GET请求的处理流程,因此会调用super.service(request, response),到这里也就找到了我们的入口程序了,好的,那么我们现在就正式开始吧!

    2:FrameworkServlet#service

    源码在org.springframework.web.servlet.FrameworkServlet,如下:

    @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // <1> HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); // <2> if (httpMethod == HttpMethod.PATCH || httpMethod == null) { processRequest(request, response); } else { // <3> super.service(request, response); } }

    <1>处代码是通过org.springframework.http.HttpMethod获取请求方法对象,是一个枚举,通过map进行维护,用到了枚举提供的values方法获取所有枚举,源码如下:

    public enum HttpMethod { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; private static final Map<String, HttpMethod> mappings = new HashMap<>(16); static { for (HttpMethod httpMethod : values()) { mappings.put(httpMethod.name(), httpMethod); } } @Nullable public static HttpMethod resolve(@Nullable String method) { return (method != null ? mappings.get(method) : null); } public boolean matches(String method) { return (this == resolve(method)); } }

    <2>处代码是单独处理PATCH请求。<3>处是我们要分析的重点,会调用父类,即javax.servlet.HttpServlet的service方法,开始这个过程。

    3:HttpServlet#service

    通过org.springframework.web.servlet.FrameworkServlet调用super.servce(req, res)会执行到javax.servlet.http.HttpServlet#service(req, res)方法,源码如下:

    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) { doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); if (ifModifiedSince < lastModified) { 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 { 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); } }

    代码中除了正常的doXXX方法调用外,主要是一些缓存相关的逻辑判断,不影响我们的流程,可忽略之,因为我们的是GET请求,则会调用doGet(req, res)方法,而该方法在org.springframework.web.servlet.FrameworkServlet中重写了,因此会调用到这里。

    4:FrameworkServlet#doGet

    源码如下:

    protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }

    直接委托给了processReqeust(req, res)方法,这里doGet其实只是起到了中转调用的作用。

    5:FrameworkServlet#processRequest

    该方法中其实也并没有什么核心逻辑,源码如下:

    protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 记录开始处理请求的开始时间,这样能够通过日志监控处理时长 long startTime = System.currentTimeMillis(); // 记录异常信息 Throwable failureCause = null; try { // <1> doService(request, response); } catch (ServletException | IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } logResult(request, response, failureCause, asyncManager); // <1> publishRequestHandledEvent(request, response, startTime, failureCause); } }

    <1>处代码为重点,该方法是一个抽象的模板方法,具体实现在类org.springframework.web.servlet.DispatcherServlet中。<2>是在请求处理完毕后发布requesthandled事件,从而记录相关请求处理相关信息,如,处理时间,处理错误信息等。

    6:DispatcherServlet#doService

    直接看源码:

    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { logRequest(request); 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)); } } } // <1> 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()); // <2> 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 { // <3> 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); } } } }

    <1>处代码是将spring相关的一些对象设置到request中,如Servlet WebApplicationContext,本地化组件等。<2>处代码是通过重定向组件来处理重定向数据,其中代码request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));中将Map数据放到一个不可修改的map中,因为只是为了传递数据,这样做可以有效的防止由于对于map的误操作导致一些bug的发生,是一种很好的编程方式,值得学习。<3>处代码是是真正处理请求的地方。

    7:DispatcherServlet#doDispatch

    可以配置这里来进行学习,看源码:

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { // <1> HttpServletRequest processedRequest = request; // <2> HandlerExecutionChain mappedHandler = null; // <3> boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { // <4> ModelAndView mv = null; // <5> Exception dispatchException = null; try { // <6> processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // <7> mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // <8> 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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // <9> if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // <10> mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } // <11> applyDefaultViewName(processedRequest, mv); // <12> mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { // <13> 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); } // <14> 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 { // <15> if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }

    <1>,<2>,<3>,<4>,<5>代码都是记录相关变量,<7>处检查是否为文件上传请求,如果是则将HttpServletRequest为MultipartHttpServletRequest,<7>通过请求获取对应的业务handler,获取方法也比较简单,直接循环所有的HandlerMapping,找到封装有对应请求handler的handlermapping,从而获取handler,源码如下:

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }

    返回的对象是org.springframework.web.servlet.HandlerExecutionChain对象,封装了目标handler+HandlerInterceptor,简单看下

    public interface HandlerInterceptor { // 在调用目标handler之前调用该方法 default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } // 在调用目标handler之后调用该方法 default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } // 在目标handler执行完毕(渲染结束)之后调用该方法(一般通过事件触发方式调用) default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } }

    一共三个方法preHandle,postHandle,afterCompletion,执行时机已经在注释中说明,可以看下。<8>处代码是获取支持handler的HandlerAdapter,获取方法和获取Handler一样也是通过循环的方式,源码如下:

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { if (this.handlerAdapters != null) { 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"); }

    其中方法support(handler)就是用来判断当前handler是否支持目标handler的,如果是支持则是我们需要的HandlerAdapter,直接返回即可。<9>为执行HandlerInterceptor的preHandle方法,注意该方法要返回true,否则后续逻辑都不会执行了,使用的过程中一定要小心。<10>处代码为调用handler(一般就是我们在controller中标注了@RequestMapping的方法),返回就是ModelAndView,<11>处代码为在没有视图的情况下,应用默认的视图名称。<12>处代码为调用HandlerInterceptor的postHandle方法。<13>为如果是发生了异常则记录异常对象到全局变量。<14>为处理请求结果,包括正常的,异常的等都会进行处理。<15>处代码为如果是文件上传请求,则处理文件处理过程中产生的临时文件。接下来看下processDispatchResult是如何处理最终的视图等信息的。

    8:DispatcherServlet#processDispatchResult

    源码:

    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { // <1> boolean errorView = false; // <2> 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); } } // <3> 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; } // <4> if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, null); } }

    <1>处为是否是异常视图的标记,如果是获取了异常视图则该值会被修改为true,<2>处代码为如果是发生了异常,则获取异常视图,一般为通过processHandlerException方法使用HandleExceptionViewResolver组件生成ModelAndView,方法源码如下:

    protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception { // Success and error responses may use different content types request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); // Check registered HandlerExceptionResolvers... ModelAndView exMv = null; if (this.handlerExceptionResolvers != null) { for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) { exMv = resolver.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()) { String defaultViewName = getDefaultViewName(request); if (defaultViewName != null) { exMv.setViewName(defaultViewName); } } if (logger.isTraceEnabled()) { logger.trace("Using resolved error view: " + exMv, ex); } if (logger.isDebugEnabled()) { logger.debug("Using resolved error view: " + exMv); } WebUtils.exposeErrorRequestAttributes(request, ex, getServletName()); return exMv; } throw ex; }

    做法也比较简单,就是通过循环HandlerExceptionResolver组件数组handlerExceptionResolvers找到错误视图ModelAndView。另外注意代码WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());``是设置相关的错误信息到request中,<3>处代码为渲染页面,后续详细分析,渲染完毕后如果是错误页面则调用WebUtils.clearErrorRequestAttributes(request);清理相关错误信息。<4>处代码为页面渲染完毕后触发HandlerInterceptor的afterCompletion方法。接下来看页面渲染方法render。

    9:DispatcherServlet#render

    源码:

    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { // <1> Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale()); response.setLocale(locale); View view; String viewName = mv.getViewName(); //<2> 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 { // <3> 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.isTraceEnabled()) { logger.trace("Rendering view [" + view + "] "); } try { // <4> if (mv.getStatus() != null) { response.setStatus(mv.getStatus().value()); } // <5> view.render(mv.getModelInternal(), request, response); } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug("Error rendering view [" + view + "]", ex); } throw ex; } }

    <1>处代码获取本地话信息,并设置到响应中,<2>处为在有viewName情况下解析出View,<3>处为没有viewName情况下直接通过ModelAndView获取View,<4>处设置响应状态码,<5>使用ViewResolver组件完成页面最终的渲染。

    最后:都让开,我要喝瑞幸

    Processed: 0.013, SQL: 8