<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring MVC 請求處理過程詳解

          共 39937字,需瀏覽 80分鐘

           ·

          2021-10-02 16:19

          點擊關(guān)注公眾號,Java干貨及時送達(dá)

          牛逼!又發(fā)現(xiàn)了一款面試題庫,太全了!!

          點擊查看

          SpringMVC請求處理相信大家都很熟悉了,本篇主要是基于SpringMVC處理請求的流程來閱讀并調(diào)試源碼,以及解決幾個僅靠流程圖無法解釋的問題。

          本篇使用的Spring版本為5.2.2.RELEASE

          九大組件

          SpringMVC幾乎所有的功能都由九大組件來完成,所以明白九大組件的作用,對于學(xué)習(xí)SpringMVC來說非常重要。

          /** 文件上傳解析器 */
          private MultipartResolver multipartResolver;

          /** 區(qū)域解析器,用于國際化 */
          private LocaleResolver localeResolver;

          /** 主題解析器 */
          private ThemeResolver themeResolver;

          /** Handler映射信息 */
          private List<HandlerMapping> handlerMappings;

          /** Handler適配器*/
          private List<HandlerAdapter> handlerAdapters;

          /** Handler執(zhí)行異常解析器 */
          private List<HandlerExceptionResolver> handlerExceptionResolvers;

          /** 請求到視圖的轉(zhuǎn)換器 */
          private RequestToViewNameTranslator viewNameTranslator;

          /** SpringMVC允許重定向時攜帶參數(shù),存在session中,用完就銷毀,所以叫FlashMap */
          private FlashMapManager flashMapManager;

          /** 視圖解析器 */
          private List<ViewResolver> viewResolvers;
          • HandlerMappingHandler映射信息,根據(jù)請求攜帶的url信息查找處理器(Handler)。每個請求都需要對應(yīng)的Handler處理。
          • HandlerAdapterHandler適配器,SpringMVC沒有直接調(diào)用處理器(Handler),而是通過HandlerAdapter來調(diào)用,主要是為了統(tǒng)一Handler的調(diào)用方式
          • ViewResolver:視圖解析器,用來將字符串類型的視圖名稱解析為View類型的視圖。ViewResolver需要找到渲染所用的模板和所用的技術(shù)(也就是視圖的類型)進(jìn)行渲染,具體的渲染過程則交由不同的視圖自己完成。
          • MultipartResolver:文件上傳解析器,主要用來處理文件上傳請求
          • HandlerExceptionResolver:Handler執(zhí)行異常解析器,用來對異常進(jìn)行統(tǒng)一處理
          • RequestToViewNameTranslator:請求到視圖的轉(zhuǎn)換器
          • LocaleResolver:區(qū)域解析器,用于支持國際化
          • FlashMapManagerSpringMVC允許重定向時攜帶參數(shù),存在session中,用完就銷毀,所以叫FlashMap
          • ThemeResolver:主題解析器,用于支持不同的主題
          九大組件中最重的的前三個,HandlerMappingHandlerAdapterViewResolver,因為這是閱讀源碼時,避不開的三個組件。

          調(diào)試準(zhǔn)備

          搭建一個基本的Spring web項目即可
          Controller部分
          @Controller
          public class IndexController {

              @RequestMapping("/index/home")
              public String home(String id, Student student, @RequestParam("code") String code) {
                  System.out.println(student.getName());
                  return "index";
              }

              @ResponseBody
              @RequestMapping("/index/list")
              public String list() {
                  return "success";
              }
          }


          Entity部分

          public class Student {

              private String name;
              private Integer gender;

             // getter、setter
          }
          還是那句話,Spring源碼非常龐大,不能只見樹木不見森林,需要有針對性的閱讀,所以本篇只需要關(guān)注主體流程即可。

          核心方法

          我們都知道,SpringMVC有一個用來分發(fā)請求的前端控制器DispatcherServlet,其中用來處理請求的方法就是doService,該方法定義如下
          doService
          /**
           * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
           * for the actual dispatching.
           */

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

           // Make framework objects available to handlers and view objects.
           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 {
            // 真正執(zhí)行的方法
            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);
             }
            }
           }
          }

          doDispatch

          doDispatchdoService中真正用來處理請求的方法
          /**
           * 實際處理請求的方法
           */

          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.
             // 為當(dāng)前請求找到一個合適的處理器(Handler)
             // 返回值是一個HandlerExecutionChain,也就是處理器執(zhí)行鏈
             mappedHandler = getHandler(processedRequest);
             if (mappedHandler == null) {
              noHandlerFound(processedRequest, response);
              return;
             }

             // Determine handler adapter for the current request.
             // 根據(jù)HandlerExecutionChain攜帶的Handler找到合適的HandlerAdapter
             HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

             // Process last-modified header, if supported by the handler.
             // 處理GET請求的緩存
             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;
              }
             }

             // 執(zhí)行攔截器的preHandle方法
             if (!mappedHandler.applyPreHandle(processedRequest, response)) {
              return;
             }

             // Actually invoke the handler.
             // 利用HandlerAdapter來執(zhí)行Handler里對應(yīng)的處理方法
             mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

             if (asyncManager.isConcurrentHandlingStarted()) {
              return;
             }

             // 如果沒有設(shè)置視圖,則應(yīng)用默認(rèn)的視圖名
             applyDefaultViewName(processedRequest, mv);
             // 執(zhí)行攔截器的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);
            }
            // 根據(jù)ModelAndView對象解析視圖
            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);
             }
            }
           }
          }


          該方法就是SpringMVC處理請求的整體流程,其中涉及到幾個重要的方法。

          getHandler

          該方法定義如下
          /**
           * Return the HandlerExecutionChain for this request.
           * 為這個request返回一個HandlerExecutionChain
           */

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


          調(diào)試信息如下

          根據(jù)調(diào)試信息可以看出,getHandler方法主要是從List<HandlerMapping> handlerMappings集合中遍歷查找一個合適的處理器(Handler),返回的結(jié)果是一個HandlerExecutionChain。然后再根據(jù)HandlerExecutionChain里攜帶的Handler去獲取HandlerAdapter

          getHandlerAdapter

          getHandlerAdapter方法定義如下
          /**
            * 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 {
            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");
           }
          調(diào)試信息如下


          同樣getHandlerAdapter方法主要是從List<HandlerAdapter> handlerAdapters集合中遍歷查找一個合適的處理器適配器(HandlerAdapter),返回的結(jié)果是一個HandlerAdapter
          可以看到此處HandlerAdapter真正的實現(xiàn)類是RequestMappingHandlerAdapter

          processDispatchResult

          processDispatchResult方法主要根據(jù)方法執(zhí)行完成后封裝的ModelAndView,轉(zhuǎn)發(fā)到對應(yīng)頁面,定義如下
          /**
           * Handle the result of handler selection and handler invocation, which is
           * either a ModelAndView or an Exception to be resolved to a ModelAndView.
           */

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

           // Did the handler return a view to render?
           if (mv != null && !mv.wasCleared()) {
            // 主要調(diào)用該方法渲染視圖
            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;
           }

           if (mappedHandler != null) {
            // Exception (if any) is already handled..
            mappedHandler.triggerAfterCompletion(request, response, null);
           }
          }

          render

          render方法定義如下
          /**
           * 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 != 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.
            // 根據(jù)給定的視圖名稱,解析獲取View對象
            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() + "'");
            }
           }

           // Delegate to the View object for rendering.
           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;
           }
          }

          resolveViewName

          resolveViewName方法定義如下

          @Nullable
          protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
            Locale locale, HttpServletRequest request) throws Exception {

           if (this.viewResolvers != null) {
            for (ViewResolver viewResolver : this.viewResolvers) {
             View view = viewResolver.resolveViewName(viewName, locale);
             if (view != null) {
              return view;
             }
            }
           }
           return null;
          }
          調(diào)試信息如下

          根據(jù)調(diào)試信息可以看到真正解析視圖的ViewResolver的是InternalResourceViewResolver類,也就是我們經(jīng)常配置的一項類型
          <!-- 定義視圖文件解析 -->
          <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
           <property name="prefix" value="/WEB-INF/views/" />
           <property name="suffix" value=".html" />
          </bean>
          至此我們就得到了SpringMVC處理請求的完整邏輯

          SpringMVC處理請求的整個流程已經(jīng)梳理清楚了。
          但是,有兩個重要的問題沒有解決,那就是:參數(shù)綁定和返回值處理。
          > 因為在編寫Controller里面的方法的時候,各種類型的參數(shù)都有,SpringMVC是怎么處理不同類型的參數(shù)的呢?
          > SpringMVC處理請求完成后,一定會返回
          ModelAndView嗎,如果加了@ResponseBody注解呢?

          參數(shù)綁定

          在整個流程中,還有一個最重要的方法,那就是真正執(zhí)行handler的方法,參數(shù)的綁定和返回值的處理都在這個方法里,也就是
          // Actually invoke the handler.
          mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

          handle

          handle方法的作用是根據(jù)請求參數(shù),執(zhí)行真正的處理方法,并且返回合適的ModelAndView對象,也有可能返回null。該方法定義如下
          AbstractHandlerMethodAdapter類中
          /**
           * This implementation expects the handler to be an {@link HandlerMethod}.
           */

          @Override
          @Nullable
          public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

           return handleInternal(request, response, (HandlerMethod) handler);
          }
          可以看到這個方法實現(xiàn)只有一行代碼

          handleInternal

          繼續(xù)深入handleInternal方法
          @Override
          protected ModelAndView handleInternal(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod)
           throws Exception
          {

           ModelAndView mav;
           // 校驗指定的請求以獲取受支持的方法類型(GET、POST等)和所需的session
           checkRequest(request);

           // Execute invokeHandlerMethod in synchronized block if required.
           if (this.synchronizeOnSession) {
            HttpSession session = request.getSession(false);
            if (session != null) {
             Object mutex = WebUtils.getSessionMutex(session);
             synchronized (mutex) {
              mav = invokeHandlerMethod(request, response, handlerMethod);
             }
            }
            else {
             // No HttpSession available -> no mutex necessary
             mav = invokeHandlerMethod(request, response, handlerMethod);
            }
           }
           else {
            // No synchronization on session demanded at all...
            // 真正執(zhí)行handler的方法
            mav = invokeHandlerMethod(request, response, handlerMethod);
           }

           if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
            if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
             applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            }
            else {
             prepareResponse(response);
            }
           }

           return mav;
          }

          invokeHandlerMethod

          繼續(xù)深入invokeHandlerMethod方法
          /**
           * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
           * if view resolution is required.
           * 執(zhí)行@RequestMapping標(biāo)注的handler方法,如果需要解析視圖就準(zhǔn)備一個ModelAndView
           */

          @Nullable
          protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod)
           throws Exception
          {

           ServletWebRequest webRequest = new ServletWebRequest(request, response);
           try {
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

            // HandlerMethod接口封裝執(zhí)行方法的信息,提供對方法參數(shù),方法返回值,方法注釋等的便捷訪問。
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
            if (this.argumentResolvers != null) {
             invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }
            if (this.returnValueHandlers != null) {
             invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }
            invocableMethod.setDataBinderFactory(binderFactory);
            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

            // ModelAndViewContainer可以看做ModelAndView的上下文容器,關(guān)聯(lián)著Model和View的信息
            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);

            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.setTaskExecutor(this.taskExecutor);
            asyncManager.setAsyncWebRequest(asyncWebRequest);
            asyncManager.registerCallableInterceptors(this.callableInterceptors);
            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

            if (asyncManager.hasConcurrentResult()) {
             Object result = asyncManager.getConcurrentResult();
             mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
             asyncManager.clearConcurrentResult();
             LogFormatUtils.traceDebug(logger, traceOn -> {
              String formatted = LogFormatUtils.formatValue(result, !traceOn);
              return "Resume with async result [" + formatted + "]";
             });
             invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }

            // 真正執(zhí)行Handler的方法
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
             return null;
            }

            // 獲取ModelAndeView對象
            return getModelAndView(mavContainer, modelFactory, webRequest);
           }
           finally {
            webRequest.requestCompleted();
           }
          }
          invokeAndHandle
          invokeAndHandle方法的作用是執(zhí)行并處理真正響應(yīng)請求的方法,該方法定義如下
          /**
           * Invoke the method and handle the return value through one of the
           * configured {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}.
           * @param webRequest the current request
           * @param mavContainer the ModelAndViewContainer for this request
           * @param providedArgs "given" arguments matched by type (not resolved)
           */

          public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
            Object... providedArgs)
           throws Exception
          {

           // 執(zhí)行handler的方法
           Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
           setResponseStatus(webRequest);

           if (returnValue == null) {
            if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
             disableContentCachingIfNecessary(webRequest);
             mavContainer.setRequestHandled(true);
             return;
            }
           }
           else if (StringUtils.hasText(getResponseStatusReason())) {
            mavContainer.setRequestHandled(true);
            return;
           }

           mavContainer.setRequestHandled(false);
           Assert.state(this.returnValueHandlers != null, "No return value handlers");
           try {
            this.returnValueHandlers.handleReturnValue(
              returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
           }
           catch (Exception ex) {
            if (logger.isTraceEnabled()) {
             logger.trace(formatErrorForReturnValue(returnValue), ex);
            }
            throw ex;
           }
          }

          invokeForRequest

          /**
           * Invoke the method after resolving its argument values in the context of the given request.
           * <p>Argument values are commonly resolved through
           * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
           * The {@code providedArgs} parameter however may supply argument values to be used directly,
           * i.e. without argument resolution. Examples of provided argument values include a
           * {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance.
           * Provided argument values are checked before argument resolvers.
           * <p>Delegates to {@link #getMethodArgumentValues} and calls {@link #doInvoke} with the
           * resolved arguments.
           * @param request the current request
           * @param mavContainer the ModelAndViewContainer for this request
           * @param providedArgs "given" arguments matched by type, not resolved
           * @return the raw value returned by the invoked method
           * @throws Exception raised if no suitable argument resolver can be found,
           * or if the method raised an exception
           * @see #getMethodArgumentValues
           * @see #doInvoke
           */

          @Nullable
          public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {

           // 獲取參數(shù)
           Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
           if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
           }
           // 執(zhí)行
           return doInvoke(args);
          }


          真正的執(zhí)行無非就是通過反射invoke,所以更重要的是參數(shù)是如何綁定的,詳情就在getMethodArgumentValues方法

          getMethodArgumentValues

          getMethodArgumentValues方法用于從request請求中獲取真正的參數(shù),返回的是Object數(shù)組,該方法定義如下
          /**
           * Get the method argument values for the current request, checking the provided
           * argument values and falling back to the configured argument resolvers.
           * <p>The resulting array will be passed into {@link #doInvoke}.
           * @since 5.1.2
           */

          protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {

           // 獲取方法上所有的參數(shù)
           MethodParameter[] parameters = getMethodParameters();
           if (ObjectUtils.isEmpty(parameters)) {
            return EMPTY_ARGS;
           }

           Object[] args = new Object[parameters.length];
           for (int i = 0; i < parameters.length; i++) {
            MethodParameter parameter = parameters[i];
            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            args[i] = findProvidedArgument(parameter, providedArgs);
            if (args[i] != null) {
             continue;
            }
            if (!this.resolvers.supportsParameter(parameter)) {
             throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
            }
            try {
             
             args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
            }
            catch (Exception ex) {
             // Leave stack trace for later, exception may actually be resolved and handled...
             if (logger.isDebugEnabled()) {
              String exMsg = ex.getMessage();
              if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
               logger.debug(formatArgumentError(parameter, exMsg));
              }
             }
             throw ex;
            }
           }
           return args;
          }



          根據(jù)調(diào)試信息可以看到,用來處理請求參數(shù)的類是HandlerMethodArgumentResolver接口的實現(xiàn)類HandlerMethodArgumentResolverComposite,此時正在處理的參數(shù)是一個Student對象,并且已經(jīng)把值注綁定了,也就是說真正執(zhí)行綁定的是方法resolveArgument

          resolveArgument

          resolveArgument是真正執(zhí)行綁定的的方法
          /**
           * Iterate over registered
           * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}
           * and invoke the one that supports it.
           * @throws IllegalArgumentException if no suitable argument resolver is found
           */

          @Override
          @Nullable
          public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

           // 獲取合適的參數(shù)解析器
           HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
           if (resolver == null) {
            throw new IllegalArgumentException("Unsupported parameter type [" +
              parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
           }
           // 執(zhí)行參數(shù)綁定
           return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
          }

          getArgumentResolver

          getArgumentResolver該方法用于執(zhí)行參數(shù)的綁定,定義如下
          /**
           * Find a registered {@link HandlerMethodArgumentResolver} that supports
           * the given method parameter.
           */

          @Nullable
          private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter)
          {
           HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
           if (result == null) {
            for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
             if (resolver.supportsParameter(parameter)) {
              result = resolver;
              this.argumentResolverCache.put(parameter, result);
              break;
             }
            }
           }
           return result;
          }


          該方法的邏輯就是先從argumentResolver緩存中找到能夠執(zhí)行參數(shù)綁定的HandlerMethodArgumentResolver,如果找不到就從HandlerMethodArgumentResolver找,SpringMVC支持的HandlerMethodArgumentResolver一共有26種,用來解析各種類型的參數(shù)


          根據(jù)博主的調(diào)試可以知道
          • RequestParamMethodArgumentResolver:處理普通參數(shù)(基本類型、包裝類型、String),不管加不加@RequestParam注解

          • ServletModelAttributeMethodProcessor:處理POJO類型的參數(shù),比如自定義的Student對象

          • RequestResponseBodyMethodProcessor:處理@RequestBody注解類型的參數(shù)

          resolveArgument

          由于不同類型的參數(shù)有不同的HandlerMethodArgumentResolver來處理,此處選取POJO類型參數(shù)的注入實現(xiàn),對應(yīng)的參數(shù)解析類是ModelAttributeMethodProcessor,其中resolveArgument方法用來解析(綁定)參數(shù)方法定義如下
          /**
           * Resolve the argument from the model or if not found instantiate it with
           * its default if it is available. The model attribute is then populated
           * with request values via data binding and optionally validated
           * if {@code @java.validation.Valid} is present on the argument.
           * @throws BindException if data binding and validation result in an error
           * and the next method parameter is not of type {@link Errors}
           * @throws Exception if WebDataBinder initialization fails
           */

          @Override
          @Nullable
          public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

           Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
           Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");

           // 獲取參數(shù)名
           String name = ModelFactory.getNameForParameter(parameter);
           // 獲取參數(shù)上的ModelAttribute注解
           ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
           if (ann != null) {
            mavContainer.setBinding(name, ann.binding());
           }

           Object attribute = null;
           BindingResult bindingResult = null;

           if (mavContainer.containsAttribute(name)) {
            attribute = mavContainer.getModel().get(name);
           }
           else {
            // Create attribute instance
            try {
             // 創(chuàng)建參數(shù)類型的實例(未注入值),底層就是通過反射調(diào)用構(gòu)造方法
             attribute = createAttribute(name, parameter, binderFactory, webRequest);
            }
            catch (BindException ex) {
             if (isBindExceptionRequired(parameter)) {
              // No BindingResult parameter -> fail with BindException
              throw ex;
             }
             // Otherwise, expose null/empty value and associated BindingResult
             if (parameter.getParameterType() == Optional.class) {
              attribute = Optional.empty();
             }
             bindingResult = ex.getBindingResult();
            }
           }

           if (bindingResult == null) {
            // Bean property binding and validation;
            // skipped in case of binding failure on construction.
            WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
            if (binder.getTarget() != null) {
             if (!mavContainer.isBindingDisabled(name)) {
              // 真正執(zhí)行綁定(值注入)的方法
              bindRequestParameters(binder, webRequest);
             }
             validateIfApplicable(binder, parameter);
             if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
              throw new BindException(binder.getBindingResult());
             }
            }
            // Value type adaptation, also covering java.util.Optional
            if (!parameter.getParameterType().isInstance(attribute)) {
             attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
            }
            bindingResult = binder.getBindingResult();
           }

           // Add resolved attribute and BindingResult at the end of the model
           Map<String, Object> bindingResultModel = bindingResult.getModel();
           mavContainer.removeAttributes(bindingResultModel);
           mavContainer.addAllAttributes(bindingResultModel);

           return attribute;
          }



          根據(jù)調(diào)試信息也可以看到bindRequestParameters(binder, webRequest)執(zhí)行完成之后,POJO類型的參數(shù)已經(jīng)完成了綁定。

          bindRequestParameters

          /**
           * This implementation downcasts {@link WebDataBinder} to
           * {@link ServletRequestDataBinder} before binding.
           * @see ServletRequestDataBinderFactory
           */

          @Override
          protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
           ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
           Assert.state(servletRequest != null, "No ServletRequest");
           ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
           // 執(zhí)行綁定的方法
           servletBinder.bind(servletRequest);
          }

          bind

          繼續(xù)深入bind方法
          public void bind(ServletRequest request) {
           // 獲取所有參數(shù)的鍵值對
           MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
           // 處理文件上傳請求
           MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
           if (multipartRequest != null) {
            bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
           }
           // 把url中攜帶的參數(shù)也加入到MutablePropertyValues
           addBindValues(mpvs, request);
           // 執(zhí)行綁定(注入值)
           doBind(mpvs);
          }


          由于調(diào)用層次過深,所以無法一步步列出下面的步驟,doBind方法的原理還是通過調(diào)用POJO對象里的setter方法設(shè)置值,可以查看最終的調(diào)試信息


          根據(jù)調(diào)試信息可以看到,最終執(zhí)行的還是POJO對象的setter方法,具體執(zhí)行的類是BeanWrapperImpl
          了解了參數(shù)的綁定,再來看返回值的處理。

          返回值處理

          invokeAndHandle

          回到源碼invokeAndHandle方法處(ServletInvocableHandlerMethod類中),該方法定義如下
          /**
           * Invoke the method and handle the return value through one of the
           * configured {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}.
           * @param webRequest the current request
           * @param mavContainer the ModelAndViewContainer for this request
           * @param providedArgs "given" arguments matched by type (not resolved)
           */

          public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
            Object... providedArgs)
           throws Exception
          {

           Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
           setResponseStatus(webRequest);

           if (returnValue == null) {
            if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
             disableContentCachingIfNecessary(webRequest);
             mavContainer.setRequestHandled(true);
             return;
            }
           }
           else if (StringUtils.hasText(getResponseStatusReason())) {
            mavContainer.setRequestHandled(true);
            return;
           }

           mavContainer.setRequestHandled(false);
           Assert.state(this.returnValueHandlers != null, "No return value handlers");
           try {
            // 真正處理不同類型返回值的方法
            this.returnValueHandlers.handleReturnValue(
              returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
           }
           catch (Exception ex) {
            if (logger.isTraceEnabled()) {
             logger.trace(formatErrorForReturnValue(returnValue), ex);
            }
            throw ex;
           }
          }
          真正處理不同類型的返回值的方法是handleReturnValue方法

          handleReturnValue

          /**
           * Iterate over registered {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers} and invoke the one that supports it.
           * @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
           */

          @Override
          public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
           throws Exception
          {

           // 根據(jù)返回值個返回值類型選取合適的HandlerMethodReturnValueHandler
           HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
           if (handler == null) {
            throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
           }
           // 真正的處理返回值
           handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
          }

          selectHandler

          @Nullable
          private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
           boolean isAsyncValue = isAsyncReturnValue(value, returnType);
           for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
            if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
             continue;
            }
            if (handler.supportsReturnType(returnType)) {
             return handler;
            }
           }
           return null;
          }



          根據(jù)調(diào)試信息可以看到,SpringMVC為返回值提供了15個HandlerMethodReturnValueHandler的實現(xiàn)了來處理不同類型的返回值。
          事實上,用來處理@ResponseBody類型的是RequestResponseBodyMethodProcessor
          如果對前文參數(shù)綁定還有印象的話,會發(fā)現(xiàn)@RequestBody類型參數(shù)綁定也是用的這個類。
          繼續(xù)跟進(jìn)RequestResponseBodyMethodProcessor類的handleReturnValue方法

          handleReturnValue

          RequestResponseBodyMethodProcessor類的handleReturnValue方法定義如下
          這里設(shè)置了一個非常重要的屬性requestHandled,這個屬性關(guān)系到是否需要返回ModelAndView對象
          @Override
          public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
            throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

           // 設(shè)置該請求是否已在處理程序中完全處理,例如@ResponseBody方法不需要視圖解析器,此處就可以設(shè)置為true。
           // 當(dāng)控制器方法聲明類型為ServletResponse或OutputStream的參數(shù)時,也可以設(shè)置此標(biāo)志為true。
           // 這個屬性設(shè)置成true之后,上層getModelAndView獲取ModelAndView時會返回Null,因為不需要視圖。
           // 默認(rèn)值為false
           mavContainer.setRequestHandled(true);
           ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
           ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

           // Try even with null return value. ResponseBodyAdvice could get involved.
           // 底層就是利用java.io.OutputStreamWriter類把返回值寫到網(wǎng)絡(luò)IO
           writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
          }
          繼續(xù)深入writeWithMessageConverters方法,一步步調(diào)試到最后,底層就是利用java.io.OutputStreamWriter類把返回值寫到網(wǎng)絡(luò)IO


          由于handleReturnValuerequestHandled設(shè)置成了true,上層在調(diào)用getModelAndView方法時會返回null,表示該請求不需要視圖。感興趣的同學(xué)自己調(diào)試一下便知。

          總結(jié)

          本文主要從源碼的閱讀和調(diào)試的角度,整體的講解了SpringMVC處理請求的整個流程,并且講解了參數(shù)的綁定以及返回值的處理。相信大家看完后,結(jié)合自己的調(diào)試信息,會對SpringMVC的請求處理過程有一個更深入的理解。
          作者:Sicimike;blog.csdn.net/Baisitao_/article/details/107471719

          如有文章對你有幫助,

          歡迎關(guān)注??、點贊??、轉(zhuǎn)發(fā)??!


          推薦, Java面試題庫,詳情點擊:
          牛逼!又發(fā)現(xiàn)了一款牛逼的Java面試題庫,史上最強!

          點擊文末“閱讀原文”可直達(dá)

          瀏覽 38
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国语对白真实视频播放 | 日本欧美中文 | 水多多www视频在线观看高清 | 91视频免费网站 | 草在线观看免费视频 |