zoukankan      html  css  js  c++  java
  • Spring MVC请求处理过程。你这样回答保证通过面试!

    前言

    SpringMVC请求处理相信大家都很熟悉了,本篇主要是基于SpringMVC处理请求的流程来阅读并调试源码,以及解决几个仅靠流程图无法解释的问题。

    关于Spring MVC的流程思维导图分享给大家:

    Spring系列的学习笔记和面试题,包含spring面试题、spring cloud面试题、spring boot面试题、spring教程笔记、spring boot教程笔记、最新阿里巴巴开发手册(63页PDF总结)、2020年Java面试手册。一共整理了1184页PDF文档。

    关注公众号:程序员白楠楠,即可获取这份1184页PDF文档的spring全家桶资料。

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

    九大组件

    SpringMVC几乎所有的功能都由九大组件来完成,所以明白九大组件的作用,对于学习SpringMVC来说非常重要。

    /** 文件上传解析器 */
    private MultipartResolver multipartResolver;
    
    /** 区域解析器,用于国际化 */
    private LocaleResolver localeResolver;
    
    /** 主题解析器 */
    private ThemeResolver themeResolver;
    
    /** Handler映射信息 */
    private List<HandlerMapping> handlerMappings;
    
    /** Handler适配器*/
    private List<HandlerAdapter> handlerAdapters;
    
    /** Handler执行异常解析器 */
    private List<HandlerExceptionResolver> handlerExceptionResolvers;
    
    /** 请求到视图的转换器 */
    private RequestToViewNameTranslator viewNameTranslator;
    
    /** SpringMVC允许重定向时携带参数,存在session中,用完就销毁,所以叫FlashMap */
    private FlashMapManager flashMapManager;
    
    /** 视图解析器 */
    private List<ViewResolver> viewResolvers;
    
    • HandlerMapping:Handler映射信息,根据请求携带的url信息查找处理器(Handler)。每个请求都需要对应的Handler处理。

    • HandlerAdapter:Handler适配器,SpringMVC没有直接调用处理器(Handler),而是通过HandlerAdapter来调用,主要是为了统一Handler的调用方式

    • ViewResolver:视图解析器,用来将字符串类型的视图名称解析为View类型的视图。ViewResolver需要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染,具体的渲染过程则交由不同的视图自己完成。

    • MultipartResolver:文件上传解析器,主要用来处理文件上传请求

    • HandlerExceptionResolver:Handler执行异常解析器,用来对异常进行统一处理

    • RequestToViewNameTranslator:请求到视图的转换器

    • LocaleResolver:区域解析器,用于支持国际化

    • FlashMapManager:SpringMVC允许重定向时携带参数,存在session中,用完就销毁,所以叫FlashMap

    • ThemeResolver:主题解析器,用于支持不同的主题

    九大组件中最重的的前三个,HandlerMapping、HandlerAdapter和ViewResolver,因为这是阅读源码时,避不开的三个组件。

    我把 Spring MVC 相关的技术文章整理成了 PDF,老规矩,关注微信公众号 Java后端 回复 666 下载。

    调试准备

    搭建一个基本的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源码非常庞大,不能只见树木不见森林,需要有针对性的阅读,所以本篇只需要关注主体流程即可。

    核心方法

    我们都知道,SpringMVC有一个用来分发请求的前端控制器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 {
      // 真正执行的方法
      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

    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 {
       // 校验是否是文件上传请求
       processedRequest = checkMultipart(request);
       multipartRequestParsed = (processedRequest != request);
    
       // Determine handler for the current request.
       // 为当前请求找到一个合适的处理器(Handler)
       // 返回值是一个HandlerExecutionChain,也就是处理器执行链
       mappedHandler = getHandler(processedRequest);
       if (mappedHandler == null) {
        noHandlerFound(processedRequest, response);
        return;
       }
    
       // Determine handler adapter for the current request.
       // 根据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;
        }
       }
    
       // 执行拦截器的preHandle方法
       if (!mappedHandler.applyPreHandle(processedRequest, response)) {
        return;
       }
    
       // Actually invoke the handler.
       // 利用HandlerAdapter来执行Handler里对应的处理方法
       mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
       if (asyncManager.isConcurrentHandlingStarted()) {
        return;
       }
    
       // 如果没有设置视图,则应用默认的视图名
       applyDefaultViewName(processedRequest, mv);
       // 执行拦截器的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);
      }
      // 根据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;
    }
    

    调试信息如下

    根据调试信息可以看出,getHandler方法主要是从List handlerMappings集合中遍历查找一个合适的处理器(Handler),返回的结果是一个HandlerExecutionChain。然后再根据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");
     }
    

    调试信息如下

    同样getHandlerAdapter方法主要是从List handlerAdapters集合中遍历查找一个合适的处理器适配器(HandlerAdapter),返回的结果是一个HandlerAdapter。

    可以看到此处HandlerAdapter真正的实现类是RequestMappingHandlerAdapter。

    processDispatchResult

    processDispatchResult方法主要根据方法执行完成后封装的ModelAndView,转发到对应页面,定义如下

    /**
     * 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()) {
      // 主要调用该方法渲染视图
      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.
      // 根据给定的视图名称,解析获取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;
    }
    

    调试信息如下

    根据调试信息可以看到真正解析视图的ViewResolver的是InternalResourceViewResolver类,也就是我们经常配置的一项类型

    <!-- 定义视图文件解析 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
     <property name="prefix" value="/WEB-INF/views/" />
     <property name="suffix" value=".html" />
    </bean>
    

    至此我们就得到了SpringMVC处理请求的完整逻辑

    SpringMVC处理请求的整个流程已经梳理清楚了。

    但是,有两个重要的问题没有解决,那就是:参数绑定和返回值处理。

    > 因为在编写Controller里面的方法的时候,各种类型的参数都有,SpringMVC是怎么处理不同类型的参数的呢? > SpringMVC处理请求完成后,一定会返回ModelAndView吗,如果加了@ResponseBody注解呢?

    参数绑定

    在整个流程中,还有一个最重要的方法,那就是真正执行handler的方法,参数的绑定和返回值的处理都在这个方法里,也就是

    // Actually invoke the handler.
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    

    handle

    handle方法的作用是根据请求参数,执行真正的处理方法,并且返回合适的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);
    }
    

    可以看到这个方法实现只有一行代码

    handleInternal

    继续深入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...
      // 真正执行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

    继续深入invokeHandlerMethod方法

    /**
     * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
     * if view resolution is required.
     * 执行@RequestMapping标注的handler方法,如果需要解析视图就准备一个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接口封装执行方法的信息,提供对方法参数,方法返回值,方法注释等的便捷访问。
      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的上下文容器,关联着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);
      }
    
      // 真正执行Handler的方法
      invocableMethod.invokeAndHandle(webRequest, mavContainer);
      if (asyncManager.isConcurrentHandlingStarted()) {
       return null;
      }
    
      // 获取ModelAndeView对象
      return getModelAndView(mavContainer, modelFactory, webRequest);
     }
     finally {
      webRequest.requestCompleted();
     }
    }
    

    invokeAndHandle

    invokeAndHandle方法的作用是执行并处理真正响应请求的方法,该方法定义如下

    /**
     * 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 {
    
     // 执行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 {
    
     // 获取参数
     Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
     if (logger.isTraceEnabled()) {
      logger.trace("Arguments: " + Arrays.toString(args));
     }
     // 执行
     return doInvoke(args);
    }
    

    真正的执行无非就是通过反射invoke,所以更重要的是参数是如何绑定的,详情就在getMethodArgumentValues方法

    getMethodArgumentValues

    getMethodArgumentValues方法用于从request请求中获取真正的参数,返回的是Object数组,该方法定义如下

    /**
     * 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 {
    
     // 获取方法上所有的参数
     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;
    }
    

    根据调试信息可以看到,用来处理请求参数的类是HandlerMethodArgumentResolver接口的实现类HandlerMethodArgumentResolverComposite,此时正在处理的参数是一个Student对象,并且已经把值注绑定了,也就是说真正执行绑定的是方法resolveArgument

    resolveArgument

    resolveArgument是真正执行绑定的的方法

    /**
     * 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 {
    
     // 获取合适的参数解析器
     HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
     if (resolver == null) {
      throw new IllegalArgumentException("Unsupported parameter type [" +
        parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
     }
     // 执行参数绑定
     return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }
    

    getArgumentResolver

    getArgumentResolver该方法用于执行参数的绑定,定义如下

    /**
     * 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缓存中找到能够执行参数绑定的HandlerMethodArgumentResolver,如果找不到就从HandlerMethodArgumentResolver找,SpringMVC支持的HandlerMethodArgumentResolver一共有26种,用来解析各种类型的参数

    根据博主的调试可以知道

    • RequestParamMethodArgumentResolver:处理普通参数(基本类型、包装类型、String),不管加不加@RequestParam注解

    • ServletModelAttributeMethodProcessor:处理POJO类型的参数,比如自定义的Student对象

    • RequestResponseBodyMethodProcessor:处理@RequestBody注解类型的参数

    resolveArgument

    由于不同类型的参数有不同的HandlerMethodArgumentResolver来处理,此处选取POJO类型参数的注入实现,对应的参数解析类是ModelAttributeMethodProcessor,其中resolveArgument方法用来解析(绑定)参数方法定义如下

    /**
     * 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");
    
     // 获取参数名
     String name = ModelFactory.getNameForParameter(parameter);
     // 获取参数上的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 {
       // 创建参数类型的实例(未注入值),底层就是通过反射调用构造方法
       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)) {
        // 真正执行绑定(值注入)的方法
        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;
    }
    

    根据调试信息也可以看到bindRequestParameters(binder, webRequest)执行完成之后,POJO类型的参数已经完成了绑定。

    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;
     // 执行绑定的方法
     servletBinder.bind(servletRequest);
    }
    

    bind

    继续深入bind方法

    public void bind(ServletRequest request) {
     // 获取所有参数的键值对
     MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
     // 处理文件上传请求
     MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
     if (multipartRequest != null) {
      bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
     }
     // 把url中携带的参数也加入到MutablePropertyValues
     addBindValues(mpvs, request);
     // 执行绑定(注入值)
     doBind(mpvs);
    }
    

    由于调用层次过深,所以无法一步步列出下面的步骤,doBind方法的原理还是通过调用POJO对象里的setter方法设置值,可以查看最终的调试信息

    根据调试信息可以看到,最终执行的还是POJO对象的setter方法,具体执行的类是BeanWrapperImpl。

    了解了参数的绑定,再来看返回值的处理。

    返回值处理

    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 {
    
     // 根据返回值个返回值类型选取合适的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;
    }
    

    根据调试信息可以看到,SpringMVC为返回值提供了15个HandlerMethodReturnValueHandler的实现了来处理不同类型的返回值。

    事实上,用来处理@ResponseBody类型的是RequestResponseBodyMethodProcessor。

    如果对前文参数绑定还有印象的话,会发现@RequestBody类型参数绑定也是用的这个类。

    继续跟进RequestResponseBodyMethodProcessor类的handleReturnValue方法

    handleReturnValue

    RequestResponseBodyMethodProcessor类的handleReturnValue方法定义如下

    这里设置了一个非常重要的属性requestHandled,这个属性关系到是否需要返回ModelAndView对象

    @Override
    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
      throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    
     // 设置该请求是否已在处理程序中完全处理,例如@ResponseBody方法不需要视图解析器,此处就可以设置为true。
     // 当控制器方法声明类型为ServletResponse或OutputStream的参数时,也可以设置此标志为true。 
     // 这个属性设置成true之后,上层getModelAndView获取ModelAndView时会返回Null,因为不需要视图。
     // 默认值为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类把返回值写到网络IO
     writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
    }
    

    继续深入writeWithMessageConverters方法,一步步调试到最后,底层就是利用java.io.OutputStreamWriter类把返回值写到网络IO

    由于handleReturnValue把requestHandled设置成了true,上层在调用getModelAndView方法时会返回null,表示该请求不需要视图。感兴趣的同学自己调试一下便知。

    总结

    本文主要从源码的阅读和调试的角度,整体的讲解了SpringMVC处理请求的整个流程,并且讲解了参数的绑定以及返回值的处理。相信大家看完后,结合自己的调试信息,会对SpringMVC的请求处理过程有一个更深入的理解。

    Spring系列的学习笔记和面试题,包含spring面试题、spring cloud面试题、spring boot面试题、spring教程笔记、spring boot教程笔记、最新阿里巴巴开发手册(63页PDF总结)、2020年Java面试手册。一共整理了1184页PDF文档。

    关注公众号:程序员白楠楠,即可获取这份1184页PDF文档的spring全家桶资料。



  • 相关阅读:
    正则表达式中?=和?:和?!的理解
    Python isinstance() 函数
    什么是REST架构
    [iOS常见问题] 关于使用QQ做第三方登录的问题!
    js与webview 常用交互代码
    AFNetworking 使用 核心代码
    NSString / NSData / char* 类型之间的转换
    集合视图 代码
    IOS面试问题总结
    UIViewController的生命周期及iOS程序执行顺序
  • 原文地址:https://www.cnblogs.com/bainannan/p/14099300.html
Copyright © 2011-2022 走看看