【Spring MVC】MVC请求的处理过程一览-HandlerMethodArgumentResolver解析参数过程【二】

1  前言

上节【Spring MVC】MVC请求的处理过程一览-HandlerMethodArgumentResolver解析参数过程【一】,我们看了六个解析器,其中 RequestParamMethodArgumentResolver、PathVariableMethodArgumentResolver、MatrixVariableMethodArgumentResolver 这三个解析器都是继承了 AbstractNamedValueMethodArgumentResolver 是带有参数值转换器的功能的,而另外三个相对应的Map 类型的 RequestParamMapMethodArgumentResolver、PathVariableMapMethodArgumentResolver、MatrixVariableMapMethodArgumentResolver 这三个解析器是没有转换功能的,本节我们继续看几个转换器。

2  解析器解析

2.1  ServletModelAttributeMethodProcessor

这个解析器也是平时常用的,他能将参数转换为我们的某个对象,比如:

// receiveByModelAttribute?orderNo=111&orderNoList=222&orderNoList=333
@GetMapping("/receiveByModelAttribute")
public OrderDto receiveByModelAttribute(@ModelAttribute OrderDto orderDto) {
    log.info("orderDto={}",  orderDto);
    return orderDto;
}

解析的效果:

上述示例也可以不写 @ModelAttribute 注解,我们回顾下它的定义其实就可以知道:

// ServletModelAttributeMethodProcessor 继承了 ModelAttributeMethodProcessor
public boolean supportsParameter(MethodParameter parameter) {
    // 含有 @ModelAttribute注解的参数 annotationNotRequired默认是 false 也就是没有 @ModelAttribute 注解的情况下不是基本类型的 不是字符串等类型的
    return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
            (this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
}

我们先看下 ServletModelAttributeMethodProcessor 的类结构,他是继续了 ModelAttributeMethodProcessor: 

那我们看下解析参数的过程 resolveArgument :

// ModelAttributeMethodProcessor#resolveArgument
// parameter 方法参数对象
// mavContainer 传递上下文的容器对象
// webRequest 请求对象
// binderFactory 绑定工厂对象
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");
    // 获取参数的名称 @ModelAttribute 注解里的 name值 没有的话就取你方法定义的参数名字 比如我们上边例子里的 orderDto
    String name = ModelFactory.getNameForParameter(parameter);
    // 是否绑定的判断 默认都会进行绑定的
    // 这个什么意思呢?比如我们上边的例子 @ModelAttribute(binding = false) OrderDto orderDto
    // 增加一个 binding = false 他就只会创建一个空的 OrderDto 对象,不进行参数值的绑定了
    // 其实就是做个标记,下边会根据这个标记判断要不要进行参数绑定
    ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
    if (ann != null) {
        mavContainer.setBinding(name, ann.binding());
    }
    // attribute 就是你的参数对象 比如 OrderDto 对象
    Object attribute = null;
    // 绑定结果
    BindingResult bindingResult = null;
    // 如果已经有了这个参数对象 attribute 那就直接取出来
    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);
        // binder.getTarget() 就是你要绑定的对象 比如上边的 OrderDto 对象
        if (binder.getTarget() != null) {
            // 这个就是判断那个标志,判断是否要进行参数绑定
            if (!mavContainer.isBindingDisabled(name)) {
                // 进行参数绑定,这里不仅仅包含绑定,也包含参数类型转换!!!
                bindRequestParameters(binder, webRequest);
            }
            // 判断参数是否含有 Validated 校验注解 有的话调用校验
            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;
}

大概分这几个步骤:

(1)获取参数名称

(2)创建参数类型对象比如上边的创建了一个 OrderDto 空对象

(3)创建当前属性的绑定对象进行参数值绑定以及转换 比如上边的 orderNo 参数,给 orderNo创建一个绑定对象,然后从请求中获取 orderNo的值,然后判断是否有转换器进行转换,最后将最终的值绑定到步骤(2)的空对象的orderNo 属性里

(4)二次判断值类型是否和方法参数类型定义是否一致,不一致进行转换器转换

其中步骤(2),ServletModelAttributeMethodProcessor 做了如下实现,也就是它首先会从请求中判断是否存在该属性的值,存在的话然后会判断有没有这样的转换器,有的话调用转换器进行对象创建,没有的话走父类的创建,通过反射获取构造器进行对象实例化创建。

// ServletModelAttributeMethodProcessor#createAttribute 实例话参数类型对象
protected final Object createAttribute(String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
    // 从请求中获取参数值
    String value = getRequestValueForAttribute(attributeName, request);
    if (value != null) {
        // 不为空的话 调用createAttributeFromRequestValue 进行创建
        Object attribute = createAttributeFromRequestValue(value, attributeName, parameter, binderFactory, request);
        // 不为空的话就用自己的,否则要用父类的创建 反射获取构造器进行创建
        if (attribute != null) {
            return attribute;
        }
    }
    return super.createAttribute(attributeName, parameter, binderFactory, request);
}
// ServletModelAttributeMethodProcessor#createAttributeFromRequestValue
protected Object createAttributeFromRequestValue(String sourceValue, String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
    // 创建参数绑定对象
    DataBinder binder = binderFactory.createBinder(request, null, attributeName);
    // 获取转换服务
    ConversionService conversionService = binder.getConversionService();
    if (conversionService != null) {
        // 直接进行参数值 到 参数类型的转换
        TypeDescriptor source = TypeDescriptor.valueOf(String.class);
        TypeDescriptor target = new TypeDescriptor(parameter);
        // 如果存在这种转换器才进行转换 否则直接返回空
        if (conversionService.canConvert(source, target)) {
            return binder.convertIfNecessary(sourceValue, parameter.getParameterType(), parameter);
        }
    }
    return null;
}

步骤(3)的话也是比较昼长,大家只要知道它是会获取转换器进行参数类型适配转换的,步骤(4)也是二次进行判断转换。

2.2  RequestResponseBodyMethodProcessor

RequestResponseBodyMethodProcessor 就是解析我们平时写的 @ResponseBody 的参数,比如:

@PostMapping("/receiveByRequestBody")
public OrderDto receiveByRequestBody(@RequestBody OrderDto orderDto) {
    log.info("orderDto={}",  orderDto);
    return orderDto;
}

效果如下:

RequestResponseBodyMethodProcessor 继承了AbstractMessageConverterMethodArgumentResolver,我们看下类图:

看下他的解析:

// RequestResponseBodyMethodProcessor#resolveArgument
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    // 获取方法参数对象
    parameter = parameter.nestedIfOptional();
    // 根据消息转换器读取参数对象
    Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
    String name = Conventions.getVariableNameForParameter(parameter);
    if (binderFactory != null) {
        // 创建绑定对象
        WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
        if (arg != null) {
            // 参数注解校验
            validateIfApplicable(binder, parameter);
            // 校验有异常的话 直接抛出
            if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
            }
        }
        if (mavContainer != null) {
            mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
        }
    }
    return adaptArgumentIfNecessary(arg, parameter);
}

其实主要就是根据消息转换器进行读取:

// RequestResponseBodyMethodProcessor#readWithMessageConverters
// webRequest 请求对象
// parameter 方法参数对象
// paramType 参数类型比如上边的 OrderDto
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter, Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
    // 请求对象
    HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
    Assert.state(servletRequest != null, "No HttpServletRequest");
    ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
    // 调用父类的读取
    Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
    if (arg == null && checkRequired(parameter)) {
        throw new HttpMessageNotReadableException("Required request body is missing: " +
                parameter.getExecutable().toGenericString(), inputMessage);
    }
    return arg;
}
// AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters
// inputMessage ServletServerHttpRequest
// parameter MethodParameter 方法参数
// targetType 参数类型 比如 OrderDto类
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableExcepti
    MediaType contentType;
    boolean noContentType = false;
    // 获取请求头里的媒体类型
    try {
        contentType = inputMessage.getHeaders().getContentType();
    }
    catch (InvalidMediaTypeException ex) {
        throw new HttpMediaTypeNotSupportedException(ex.getMessage());
    }
    // 没有的话 默认为application/octet-stream
    if (contentType == null) {
        noContentType = true;
        contentType = MediaType.APPLICATION_OCTET_STREAM;
    }
    // 方法参数所在的类 比如某个 Controller 类
    Class<?> contextClass = parameter.getContainingClass();
    // 目标类 比如我们的 OrderDto 类
    Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
    if (targetClass == null) {
        ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
        targetClass = (Class<T>) resolvableType.resolve();
    }
    // 请求方法
    HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
    Object body = NO_VALUE;
    EmptyBodyCheckingHttpInputMessage message = null;
    try {
        message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
        // 遍历消息转换器 json形式的默认的转换器是 MappingJackson2HttpMessageConverter
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
            GenericHttpMessageConverter<?> genericConverter =
                    (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
            if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
                    (targetClass != null && converter.canRead(targetClass, contentType))) {
                // 读取
                if (message.hasBody()) {
                    // 前置处理
                    HttpInputMessage msgToUse = getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                    // 读取
                    body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) : ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                    // 后置处理
                    body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
                }
                else {
                    body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
                }
                break;
            }
        }
    }
    catch (IOException ex) {
        throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
    }
    finally {
        if (message != null && message.hasBody()) {
            closeStreamIfNecessary(message.getBody());
        }
    }
    // 空值检查
    if (body == NO_VALUE) {
        if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) || (noContentType && !message.hasBody())) {
            return null;
        }
        throw new HttpMediaTypeNotSupportedException(contentType,
                getSupportedMediaTypes(targetClass != null ? targetClass : Object.class));
    }
    // 打印日志
    MediaType selectedContentType = contentType;
    Object theBody = body;
    LogFormatUtils.traceDebug(logger, traceOn -> {
        String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
        return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
    });
    return body;
}

最后其实就是 MappingJackson2HttpMessageConverter 这个消息转换器工作的,就跟我们平时使用 jackson 一样的:

objectMapper.readValue(inputStream, javaType); 

2.3  ServletRequestMethodArgumentResolver

ServletRequestMethodArgumentResolver 支持填充的类型还是比较多的:

// ServletRequestMethodArgumentResolver#supportsParameter()
public boolean supportsParameter(MethodParameter parameter) {
    Class<?> paramType = parameter.getParameterType();
    return (WebRequest.class.isAssignableFrom(paramType) ||
            ServletRequest.class.isAssignableFrom(paramType) ||
            MultipartRequest.class.isAssignableFrom(paramType) ||
            HttpSession.class.isAssignableFrom(paramType) ||
            (pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
            (Principal.class.isAssignableFrom(paramType) && !parameter.hasParameterAnnotations()) ||
            InputStream.class.isAssignableFrom(paramType) ||
            Reader.class.isAssignableFrom(paramType) ||
            HttpMethod.class == paramType ||
            Locale.class == paramType ||
            TimeZone.class == paramType ||
            ZoneId.class == paramType);
}

使用方式比如:

@GetMapping("/testServlet")
public void testServlet(HttpServletRequest request, HttpServletResponse response) {
    log.info("request={}, response={}",  request, response);
}

效果如下:

看下他的解析:

// ServletRequestMethodArgumentResolver#resolveArgument
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    // 参数类型
    Class<?> paramType = parameter.getParameterType();
    // 要填充  WebRequest 的话
    // WebRequest / NativeWebRequest / ServletWebRequest
    if (WebRequest.class.isAssignableFrom(paramType)) {
        if (!paramType.isInstance(webRequest)) {
            throw new IllegalStateException(
                    "Current request is not of type [" + paramType.getName() + "]: " + webRequest);
        }
        return webRequest;
    }
    // 填充 ServletRequest、MultipartRequest
    // ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest
    if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {
        return resolveNativeRequest(webRequest, paramType);
    }
    // 处理其他类型
    // HttpServletRequest required for all further argument types
    return resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));
}

2.4  ServletResponseMethodArgumentResolver

ServletResponseMethodArgumentResolver 支持三种类型的参数,如下:

// ServletResponseMethodArgumentResolver#supportsParameter()
public boolean supportsParameter(MethodParameter parameter) {
    Class<?> paramType = parameter.getParameterType();
    return (ServletResponse.class.isAssignableFrom(paramType) ||
            OutputStream.class.isAssignableFrom(paramType) ||
            Writer.class.isAssignableFrom(paramType));
}

我们看下他的解析:

// ServletResponseMethodArgumentResolver#resolveArgument()
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    if (mavContainer != null) {
        mavContainer.setRequestHandled(true);
    }
    Class<?> paramType = parameter.getParameterType();
    // ServletResponse, HttpServletResponse
    if (ServletResponse.class.isAssignableFrom(paramType)) {
        return resolveNativeResponse(webRequest, paramType);
    }
    // ServletResponse required for all further argument types
    return resolveArgument(paramType, resolveNativeResponse(webRequest, ServletResponse.class));
}

3  小结

好啦,本节我们主要看了四个解析器,有理解不对的地方还请指正哈。

posted @ 2025-06-22 11:48  酷酷-  阅读(29)  评论(0)    收藏  举报