框架进行时——SpringMVC流程简析(一)

基于 SpringWeb(5.3.23)的接口请求分析

1、案例说明

假定当前 Web 项目中有如下实体类和接口:

package com.example.entity;

public class WebUser {
  private String name;
  private Integer age;
  private LocalDate birthday;
  private Boolean gender;
  
  // getter、setter、toString ...
}
package com.example.controller;

@RestController @RequestMapping("/test")
public class WebController {

  @RequestMapping("/1")
  public WebUser test1(HttpServletRequest request, @RequestParam Map<String, Object> params, WebUser webUser) {
    System.out.println("request.getClass() = " + request.getClass());
    System.out.println("params = " + params);
    System.out.println("webUser = " + webUser);
    return webUser;
  }
}

使用 Postman 发送请求测试,结果符合预期:
postman截图
服务端日志

下面就一些关键点进行探究分析。

2、请求处理的整体流程总览

注意到 org.springframework.web.servlet.DispatcherServlet#doDispatch 方法中有如下代码片段:

// Determine handler for the current request.
HandlerExecutionChain mappedHandler = getHandler(processedRequest);

// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

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

即总的来说,处理请求可以分三步:获取处理器、获取适配器、使用适配器执行处理器。

2.1 获取处理器

SpringMVC 中定义了一个“处理器映射规则”接口 HandlerMapping,用来根据特定的请求返回对应的处理器(handler)。源码如下:

package org.springframework.web.servlet;

public interface HandlerMapping {
  
  // 根据具体的请求返回一个处理器执行器链
  HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

这里的返回值是一个处理器执行器链 HandlerExecutionChain ,它由处理器和围绕该处理器的所有处理器拦截器 HandlerInterceptor 组成,是对处理器的一层封装(下文中“处理器”一词也代指“处理器执行器链”,如无特殊说明不再区分)。

SpringMVC 中默认的处理器映射规则有以下5种:

0 = {RequestMappingHandlerMapping@7031} (order=0)
1 = {BeanNameUrlHandlerMapping@7032} (order=2)
2 = {RouterFunctionMapping@7033} (order=3)
3 = {SimpleUrlHandlerMapping@7034} (order=2147483646)
4 = {WelcomePageHandlerMapping@7035} (order=2147483647)

获取处理器,就是按照次序遍历所有的处理器映射规则,挨个尝试获取处理器,直至找到第一个非空的处理器:

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

最常用的处理器映射规则就是第一种 RequestMappingHandlerMapping,本案例使用到的也是它。由它获取到的处理器的类型是 HandlerMethod

2.2 获取适配器

处理器需要由处理器适配器 HandlerAdapter 来执行。处理器适配器的接口声明如下:

package org.springframework.web.servlet;

public interface HandlerAdapter {
  // 判断是否支持给定的处理器
  boolean supports(Object handler);

  // 使用给定的处理器对本次请求进行处理
  ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}

SpringMVC 中默认的处理器适配器有如下4个:

0 = {RequestMappingHandlerAdapter@6792}
1 = {HandlerFunctionAdapter@6793}
2 = {HttpRequestHandlerAdapter@6794}
3 = {SimpleControllerHandlerAdapter@6795}

它们各自对 supports 方法的实现如下:

  • RequestMappingHandlerAdapter(order=2147483647)

    public final boolean supports(Object handler) {
      return (handler instanceof HandlerMethod);
    }
    

    即当处理器类型为 org.springframework.web.method.HandlerMethod 时支持处理。

  • HandlerFunctionAdapter(order=2147483647)

    public boolean supports(Object handler) {
      return (handler instanceof HandlerFunction);
    }
    

    即当处理器类型为 org.springframework.web.servlet.function.HandlerFunction 时支持处理。

  • HttpRequestHandlerAdapter

    public boolean supports(Object handler) {
      return (handler instanceof HttpRequestHandler);
    }
    

    即当处理器类型为 org.springframework.web.HttpRequestHandler 时支持处理。

  • SimpleControllerHandlerAdapter

    public boolean supports(Object handler) {
      return (handler instanceof Controller);
    }
    

    即当处理器类型为 org.springframework.web.servlet.mvc.Controller 时支持处理。

获取适配器的过程,就是遍历所有的适配器,找到第一个支持当前处理器的适配器:

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

结合上文中提到 RequestMappingHandlerMapping 返回的处理器类型 HandlerMethod 正好与第一个适配器 RequestMappingHandlerAdapter 支持的类型相符,因此这一适配器会被选中。实际上它也是开发中最常用的一个处理器适配器。

2.3 执行处理器

RequestMappingHandlerAdapter#handle 方法会调用类内部的 invokeHandlerMethod 方法,其核心代码如下:

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  // "可执行的"方法
  ServletInvocableHandlerMethod invocableMethod 
      = createInvocableHandlerMethod(handlerMethod);
  // 为"可执行的方法"添加参数解析器(HandlerMethodArgumentResolverComposite)
  if (this.argumentResolvers != null) {
    invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
  }
  // 为"可执行的方法"添加返回值处理器(HandlerMethodReturnValueHandlerComposite)
  if (this.returnValueHandlers != null) {
    invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
  }
  // 为"可执行的方法"添加数据绑定器(ServletRequestDataBinderFactory)      
  invocableMethod.setDataBinderFactory(binderFactory);
  // "可执行的方法"开始执行
  invocableMethod.invokeAndHandle(webRequest, mavContainer);
    
  return getModelAndView(mavContainer, modelFactory, webRequest);
}

接下来进入到 ServletInvocableHandlerMethod 类中,查看其 invokeAndHandle 方法,核心代码如下:

public void invokeAndHandle(ServletWebRequest webRequest, 
                            ModelAndViewContainer mavContainer,
                            Object... providedArgs) throws Exception {
  // 执行目标方法,获得返回结果
  Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
  // 对返回结果进行处理
  this.returnValueHandlers.handleReturnValue(
            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}

3、接口参数的赋值逻辑

在案例中,接口可直接使用定义的三个参数 request、params 和 webUser,但开发的代码中并没有显示地为它们赋值——这是由 SpringMVC 的参数解析器自动完成的。

3.1 流程概览

继续进入到 InvocableHandlerMethod#invokeForRequest 方法中,其核心代码如下:

public Object invokeForRequest(NativeWebRequest request, 
                               ModelAndViewContainer mavContainer,
                               Object... providedArgs) throws Exception {
  // 获取方法参数值
  Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
  // 利用反射机制,使用获得的参数值执行目标方法
  return doInvoke(args);
}

此处得到的 args 就是目标方法的三个实际参数(request, params 和 webUser)。

于是查看 getMethodArgumentValues 方法的实现:

protected Object[] getMethodArgumentValues(NativeWebRequest request,
                                           ModelAndViewContainer mavContainer,
                                           Object... providedArgs) throws Exception {
  // 获取方法参数列表
  MethodParameter[] parameters = getMethodParameters();
  // 准备数组保存参数值。其长度与参数列表长度一致
  Object[] args = new Object[parameters.length];
  for (int i = 0; i < parameters.length; i++) {
    // 遍历参数列表
    MethodParameter parameter = parameters[i];
    // 如果参数解析器不支持解析当前参数,则抛出异常
    if (!this.resolvers.supportsParameter(parameter)) {
      throw new IllegalStateException(
          "No suitable resolver: Could not resolve parameter XXX");
    }
    // 使用参数解析器来解析当前参数
    args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
    }
    return args;
}

3.2 参数解析逻辑

SpringMVC 中定义了多种参数解析器 HandlerMethodArgumentResolver,特定的解析器支持在特定的条件下为参数赋值。参数解析器的接口声明如下:

package org.springframework.web.method.support;

public interface HandlerMethodArgumentResolver {
  // 是否支持这样的参数,即当参数满足什么样的条件时可以为其赋值
  boolean supportsParameter(MethodParameter parameter);
  
  // 如何为参数赋值
  Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}

参数解析器有一个重要的组合器实现HandlerMethodArgumentResolverComposite,上文中提到的 InvocableHandlerMethod 中的 resolvers 也正是它,它维护了其他的参数解析器,其核心代码如下:

package org.springframework.web.method.support;

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver { 
  // 管理所有的参数解析器
  private final List<HandlerMethodArgumentResolver> argumentResolvers 
      = new ArrayList<>();
  // 缓存后不必每次都遍历所有
  private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache 
      = new ConcurrentHashMap<>(256);
  
  // 检查所管理的参数解析器,看其中是否有支持的
  @Override
  public boolean supportsParameter(MethodParameter parameter) {
    return getArgumentResolver(parameter) != null;
  }

  // 用第一个支持此参数的解析器来处理
  @Override @Nullable
  public Object resolveArgument(MethodParameter parameter,  
                                ModelAndViewContainer mavContainer, 
                                NativeWebRequest webRequest,  
                                WebDataBinderFactory binderFactory) throws Exception {
    HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
    if (resolver == null) {
        // 如果提前调用过 supportsParameter 方法判断,则此处不可能为 null
      throw new IllegalArgumentException("Unsupported parameter type ${parameterTypeName}. supportsParameter should be called first.");
    }
    return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
  }

  // 遍历所有已注册的参数解析器,找到第一个支持这种参数的。如果未找到则返回 null
  @Nullable
  private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
    HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    if (result != null) {
      return result;
    }
    for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
      if (resolver.supportsParameter(parameter)) {
        this.argumentResolverCache.put(parameter, resolver);
        return resolver;
      }
    }
    // 通常来说不可能走到这里,因为最后一个解析器 ServletModelAttributeMethodProcessor 是几乎“万能”的
  }
}

默认的参数解析器(此处的 this.argumentResolvers)有27个,它们各自对 supportsParameter 方法的实现如下(按顺序排列):

  • 0 = {RequestParamMethodArgumentResolver@6892}

    /*
     * 满足以下三类条件中的任意一类:
     *   1. 参数标有 @RequestParam 注解、且类型不是 Map 或其子类
     *   2. 参数标有 @RequestParam 注解、且类型是 Map 或其子类、且 @RequestParam 注解的"name"或"value"属性不为空
     *   3. 参数类型是 MultipartFile 或 Part(包括它们的数组或集合)、且没有标 @RequestPart 注解
     */
    public boolean supportsParameter0(MethodParameter parameter) {
      if (parameter.hasParameterAnnotation(RequestParam.class)) {
        if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
          RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
          return (requestParam != null && StringUtils.hasText(requestParam.name()));
        } else {
          return true;
        }
      } else {
        if (parameter.hasParameterAnnotation(RequestPart.class)) {
          return false;
        }
        parameter = parameter.nestedIfOptional();
        return MultipartResolutionDelegate.isMultipartArgument(parameter);
      }
    }
    
  • 1 = {RequestParamMapMethodArgumentResolver@6893}

    /*
     * 同时满足以下三个条件:
     *   1. 参数类型是 Map 或其子类
     *   2. 参数标有 @RequestParam 注解
     *   3. @RequestParam 注解未设置"name"或"value"属性
     */
    public boolean supportsParameter(MethodParameter parameter) {
      RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
      return requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) && !StringUtils.hasText(requestParam.name());
    }
    
  • 2 = {PathVariableMethodArgumentResolver@6894}

    /*
     * 满足以下两类条件中的任意一类:
     *   1. 参数标有 @PathVariable 注解、且类型不是 Map 或其子类
     *   2. 参数标有 @PathVariable 注解、且类型是 Map 或其子类、且 @PathVariable 注解的"name"或"value"属性不为空
     */
    public boolean supportsParameter(MethodParameter parameter) {
      if (!parameter.hasParameterAnnotation(PathVariable.class)) {
        return false;
      }
      if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
        PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
        return (pathVariable != null && StringUtils.hasText(pathVariable.value()));
      }
      return true;
    }
    
  • 3 = {PathVariableMapMethodArgumentResolver@6895}

    /*
     * 同时满足以下三个条件:
     *   1. 参数类型是 Map 或其子类
     *   2. 参数标有 @PathVariable 注解
     *   3. @PathVariable 注解未设置"name"或"value"属性
     */
    public boolean supportsParameter(MethodParameter parameter) {
      PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
      return (pathVariable != null && Map.class.isAssignableFrom(parameter.getParameterType()) && !StringUtils.hasText(pathVariable.value()));
    }
    
  • 4 = {MatrixVariableMethodArgumentResolver@6896}

    /*
     * 满足以下两类条件中的任意一类:
     *   1. 参数标有 @MatrixVariable 注解、且类型不是 Map 或其子类
     *   2. 参数标有 @MatrixVariable 注解、且类型是 Map 或其子类、且 @MatrixVariable 注解的"name"或"value"属性不为空
     */
    public boolean supportsParameter(MethodParameter parameter) {
      if (!parameter.hasParameterAnnotation(MatrixVariable.class)) {
        return false;
      }
      if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
        MatrixVariable matrixVariable = parameter.getParameterAnnotation(MatrixVariable.class);
        return (matrixVariable != null && StringUtils.hasText(matrixVariable.name()));
      }
      return true;
    }
    
  • 5 = {MatrixVariableMapMethodArgumentResolver@6897}

    /*
     * 同时满足以下三个条件:
     *   1. 参数类型是 Map 或其子类
     *   2. 参数标有 @MatrixVariable 注解
     *   3. @MatrixVariable 注解未设置"name"或"value"属性
     */
    public boolean supportsParameter(MethodParameter parameter) {
      MatrixVariable matrixVariable = parameter.getParameterAnnotation(MatrixVariable.class);
      return (matrixVariable != null && Map.class.isAssignableFrom(parameter.getParameterType()) && !StringUtils.hasText(matrixVariable.name()));
    }
    
  • 6 = {ServletModelAttributeMethodProcessor@6898}
    ModelAttributeMethodProcessor

    // 参数标有 @ModelAttribute 注解
    public boolean supportsParameter(MethodParameter parameter) {
      return parameter.hasParameterAnnotation(ModelAttribute.class);
    }
    
  • 7 = {RequestResponseBodyMethodProcessor@6899}

    // 参数标有 @RequestBody 注解
    public boolean supportsParameter(MethodParameter parameter) {
      return parameter.hasParameterAnnotation(RequestBody.class);
    }
    
  • 8 = {RequestPartMethodArgumentResolver@6900}

    /*
     * 满足以下两类条件中的任意一类:
     *   1. 参数标有 @RequestPart 注解
     *   2. 参数类型是 MultipartFile 或 Part(包括它们的数组或集合)、且没有标 @RequestParam 注解
     */
    public boolean supportsParameter(MethodParameter parameter) {
      if (parameter.hasParameterAnnotation(RequestPart.class)) {
        return true;
      }
      if (parameter.hasParameterAnnotation(RequestParam.class)) {
        return false;
      }
      return MultipartResolutionDelegate.isMultipartArgument(parameter.nestedIfOptional());
    }
    
  • 9 = {RequestHeaderMethodArgumentResolver@6901}

    /*
     * 同时满足以下两个条件:
     *   1. 参数标有 @RequestHeader 注解
     *   2. 参数类型不是 Map 或其子类
     */
    public boolean supportsParameter(MethodParameter parameter) {
      return (parameter.hasParameterAnnotation(RequestHeader.class) && !Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType()));
    }
    
  • 10 = {RequestHeaderMapMethodArgumentResolver@6902}

    /*
     * 同时满足以下两个条件:
     *   1. 参数标有 @RequestHeader 注解
     *   2. 参数类型是 Map 或其子类
     */
    public boolean supportsParameter(MethodParameter parameter) {
      return (parameter.hasParameterAnnotation(RequestHeader.class) && Map.class.isAssignableFrom(parameter.getParameterType()));
    }
    
  • 11 = {ServletCookieValueMethodArgumentResolver@6903}
    AbstractCookieValueMethodArgumentResolver

    // 参数标有 @CookieValue 注解
    public boolean supportsParameter(MethodParameter parameter) {
      return parameter.hasParameterAnnotation(CookieValue.class);
    }
    
  • 12 = {ExpressionValueMethodArgumentResolver@6904}

    // 参数标有 @Value 注解
    public boolean supportsParameter(MethodParameter parameter) {
      return parameter.hasParameterAnnotation(Value.class);
    }
    
  • 13 = {SessionAttributeMethodArgumentResolver@6905}

    // 参数标有 @SessionAttribute 注解
    public boolean supportsParameter(MethodParameter parameter) {
      return parameter.hasParameterAnnotation(SessionAttribute.class);
    }
    
  • 14 = {RequestAttributeMethodArgumentResolver@6906}

    // 参数标有 @RequestAttribute 注解
    public boolean supportsParameter(MethodParameter parameter) {
      return parameter.hasParameterAnnotation(RequestAttribute.class);
    }
    
  • 15 = {ServletRequestMethodArgumentResolver@6907}

    /*
     * 满足以下三类条件中的任意一类:
     *   1. 参数类型 Principal 或其子类,且没有标任何注解
     *   2. 参数类型是 WebRequest、ServletRequest、MultipartRequest、HttpSession、PushBuilder、InputStream 或 Reader 或它们的子类
     *   2. 参数类型是 HttpMethod、Locale、TimeZone 或 ZoneId
     */
    public boolean supportsParameter(MethodParameter parameter) {
      Class<?> paramType = parameter.getParameterType();
      return (Principal.class.isAssignableFrom(paramType) && !parameter.hasParameterAnnotations())
          || (WebRequest||ServletRequest||MultipartRequest||HttpSession||PushBuilder||InputStream||Reader).class.isAssignableFrom(paramType)
          || (HttpMethod||Locale||TimeZone||ZoneId).class == paramType;
    }
    
  • 16 = {ServletResponseMethodArgumentResolver@6908}

    // 参数类型是 ServletResponse、OutputStream 或 Writer 或它们的子类
    public boolean supportsParameter(MethodParameter parameter) {
      return (ServletResponse || OutputStream || Writer).class.isAssignableFrom(parameter.getParameterType());
    }
    
  • 17 = {HttpEntityMethodProcessor@6909}

    // 参数类型是 HttpEntity 或 RequestEntity
    public boolean supportsParameter(MethodParameter parameter) {
      Class<?> parameterType = parameter.getParameterType();
      return (parameterType == HttpEntity.class)  || (parameterType == RequestEntity.class);
    }
    
  • 18 = {RedirectAttributesMethodArgumentResolver@6910}

    // 参数类型是 RedirectAttributes 或其子类
    public boolean supportsParameter(MethodParameter parameter) {
      return RedirectAttributes.class.isAssignableFrom(parameter.getParameterType());
    }
    
  • 19 = {ModelMethodProcessor@6911}

    // 参数类型是 Model 或其子类
    public boolean supportsParameter(MethodParameter parameter) {
      return Model.class.isAssignableFrom(parameter.getParameterType());
    }
    
  • 20 = {MapMethodProcessor@6912}

    /*
     * 同时满足以下两个条件:
     *   1. 参数类型是 Map 或其子类
     *   2. 参数没有标任何注解
     */
    public boolean supportsParameter(MethodParameter parameter) {
      return (Map.class.isAssignableFrom(parameter.getParameterType()) && parameter.getParameterAnnotations().length == 0);
    }
    
  • 21 = {ErrorsMethodArgumentResolver@6913}

    // 参数类型是 Errors 或其子类
    public boolean supportsParameter(MethodParameter parameter) {
      return Errors.class.isAssignableFrom(parameter.getParameterType());
    }
    
  • 22 = {SessionStatusMethodArgumentResolver@6914}

    // 参数类型是 SessionStatus 或其子类
    public boolean supportsParameter(MethodParameter parameter) {
      return parameter.getParameterType() == SessionStatus.class;
    }
    
  • 23 = {UriComponentsBuilderMethodArgumentResolver@6915}

    // 参数类型是 UriComponentsBuilder 或 ServletUriComponentsBuilder
    public boolean supportsParameter(MethodParameter parameter) {
      Class<?> parameterType = parameter.getParameterType();
      return (parameterType == UriComponentsBuilder.class)  || (parameterType == ServletUriComponentsBuilder.class);
    }
    
  • 24 = {PrincipalMethodArgumentResolver@6916}

    // 参数类型是 Principal 或其子类
    public boolean supportsParameter(MethodParameter parameter) {
      return Principal.class.isAssignableFrom(parameter.getParameterType());
    }
    
  • 25 = {RequestParamMethodArgumentResolver@6917}

    /*
     * 满足以下四类条件中的任意一类:
     *   1. 参数标有 @RequestParam 注解、且类型不是 Map 或其子类
     *   2. 参数标有 @RequestParam 注解、且类型是 Map 或其子类、且 @RequestParam 注解的"name"或"value"属性不为空
     *   3. 参数类型是 MultipartFile 或 Part(包括它们的数组或集合)、且没有标 @RequestPart 注解
     *   4. 参数类型不简单、且没有标 @RequestPart 注解
     */
    public boolean supportsParameter25(MethodParameter parameter) {
      if (parameter.hasParameterAnnotation(RequestParam.class)) {
        if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
          RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
          return (requestParam != null && StringUtils.hasText(requestParam.name()));
        } else {
          return true;
        }
      } else {
        if (parameter.hasParameterAnnotation(RequestPart.class)) {
          return false;
        }
        parameter = parameter.nestedIfOptional();
        if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
          return true;
        } else {
          return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
        }
      }
    }
    
  • 26 = {ServletModelAttributeMethodProcessor@6918}

    /*
     * 满足以下两个条件中的任意一个:
     *   1. 参数标有 @ModelAttribute 注解
     *   2. 参数类型不简单
     */
    public boolean supportsParameter(MethodParameter parameter) {
      return (parameter.hasParameterAnnotation(ModelAttribute.class)) || (!BeanUtils.isSimpleProperty(parameter.getParameterType()));
    }
    

逐个分析可知,案例中接口的参数:

  • request:其类型是 HttpServletRequest,即 Servlet 的子类,因此会被15号解析器ServletRequestMethodArgumentResolver处理。
  • params:其类型是 Map,且标有未设置属性的 @RequestParam 注解,因此会被1号解析器RequestParamMapMethodArgumentResolver处理。
  • webUser:该参数不是简单类型,且没有标任何注解,只能被最后一个解析器ServletModelAttributeMethodProcessor处理。
    接下来调用各自的 resolveArgument 方法即获得参数值
    ... ...

3.3 webUser 的赋值

重点看一下 ServletModelAttributeMethodProcessorresolveArgument 方法:

public final Object resolveArgument(MethodParameter parameter, 
                                    ModelAndViewContainer mavContainer,
                                    NativeWebRequest webRequest, 
                                    WebDataBinderFactory binderFactory) throws Exception {
  // 获取参数名称。此处即为 "webUser"
  String name = ModelFactory.getNameForParameter(parameter);
  // TODO 当参数标有 @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();
      } else {
        attribute = ex.getTarget();
      }
      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;
}
posted @   Sept4_桃李宿江南  阅读(139)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示